diff --git a/README.md b/README.md index 5dc6d482..3f84bce2 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,49 @@ func main() { } ``` +# Generate support for third-party modules +This is an example that takes the path of a third-party module source code to generate support for it. Assume the source code path of that module is `./src`. You can call `go run cmd/generate/main.go ./src`. The stdout will be like + +```go +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package crossplane + +var directives = map[string][]uint{ + "my_directive_1": { + bitmask01|bitmask02|..., + bitmask11|bitmask12|..., + ... + }, + "my_directive_2": { + bitmask01|bitmask02|..., + bitmask11|bitmask12|..., + ... + }, +} + +// Match is a matchFunc for parsing an NGINX config that contains the +// preceding directives. +func Match(directive string) ([]uint, bool) { + m, ok := directives[directive] + return m, ok +} +``` +You can redirect the stdout into a `.go` file, and pass the generated `matchFunc` to `ParseOptions.DirectiveSources` when invoking `Parse`. + ## Contributing If you'd like to contribute to the project, please read our [Contributing guide](CONTRIBUTING.md). diff --git a/cmd/generate/main.go b/cmd/generate/main.go new file mode 100644 index 00000000..8b416f4e --- /dev/null +++ b/cmd/generate/main.go @@ -0,0 +1,28 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +package main + +import ( + "flag" + "log" + "os" + + "github.com/nginxinc/nginx-go-crossplane/internal/generator" +) + +func main() { + var ( + sourceCodePath = flag.String("src-path", "", + "the path of source code your want to generate support from, it can be either a file or a directory. (required)") + ) + flag.Parse() + err := generator.Generate(*sourceCodePath, os.Stdout) + if err != nil { + log.Fatal(err) + } +} diff --git a/go.mod b/go.mod index 80264c05..a68001e1 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 93276928..acff8f8e 100644 --- a/go.sum +++ b/go.sum @@ -27,11 +27,12 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/generator/generator.go b/internal/generator/generator.go new file mode 100644 index 00000000..145c6b7a --- /dev/null +++ b/internal/generator/generator.go @@ -0,0 +1,20 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +package generator + +import ( + "io" +) + +// Generate receives a string sourcePath and an io.Writer writer. It will +// extract all the directives definitions from the .c and .cpp files in +// sourcePath and its subdirectories, then output the corresponding directive +// masks map named "directives" and matchFunc named "Match" via writer. +func Generate(sourcePath string, writer io.Writer) error { + return genFromSrcCode(sourcePath, "directives", "Match", writer) +} diff --git a/internal/generator/generator_util.go b/internal/generator/generator_util.go new file mode 100644 index 00000000..48a415de --- /dev/null +++ b/internal/generator/generator_util.go @@ -0,0 +1,203 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +package generator + +import ( + _ "embed" + "errors" + "fmt" + "html/template" + "io" + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" +) + +// A mask is a list of string, includes several variable names, +// which specify a behavior of a directive. +// An example is []string{"ngxHTTPMainConf", "ngxConfFlag",}. +// A directive can have several masks. +type mask []string + +type supportFileTmplStruct struct { + Directive2Masks map[string][]mask + MapVariableName string + MatchFnName string +} + +var ( + // Extract single directive definition block + // static ngx_command_t {name}[] = {definition} + // this regex extracts {name} and {definition}. + directivesDefineBlockExtracter = regexp.MustCompile(`ngx_command_t\s+(\w+)\[\]\s*=\s*{(.*?)}\s*;`) + + // Extract one directive definition and attributes from extracted block + // { ngx_string({directive_name}), + // {bitmask1|bitmask2|...}, + // ... }, + // this regex extracts {directive_name} and {bitmask1|bitmask2|...}. + singleDirectiveExtracter = regexp.MustCompile(`ngx_string\("(.*?)"\).*?,(.*?),`) + + singleLineCommentExtracter = regexp.MustCompile(`//.*`) + + multiLineCommentExtracter = regexp.MustCompile(`/\*[\s\S]*?\*/`) +) + +// Template of support file. A support file contains a map from +// diective to its bitmask definitions, and a MatchFunc for it. +// +//go:embed tmpl/support_file.tmpl +var supportFileTmplStr string + +//nolint:gochecknoglobals +var supportFileTmpl = template.Must(template.New("supportFile"). + Funcs(template.FuncMap{"Join": strings.Join}).Parse(supportFileTmplStr)) + +//nolint:gochecknoglobals +var ngxVarNameToGo = map[string]string{ + "NGX_MAIL_MAIN_CONF": "ngxMailMainConf", + "NGX_STREAM_MAIN_CONF": "ngxStreamMainConf", + "NGX_CONF_TAKE1": "ngxConfTake1", + "NGX_STREAM_UPS_CONF": "ngxStreamUpsConf", + "NGX_HTTP_LIF_CONF": "ngxHTTPLifConf", + "NGX_CONF_TAKE2": "ngxConfTake2", + "NGX_HTTP_UPS_CONF": "ngxHTTPUpsConf", + "NGX_CONF_TAKE23": "ngxConfTake23", + "NGX_CONF_TAKE12": "ngxConfTake12", + "NGX_HTTP_MAIN_CONF": "ngxHTTPMainConf", + "NGX_HTTP_LMT_CONF": "ngxHTTPLmtConf", + "NGX_CONF_TAKE1234": "ngxConfTake1234", + "NGX_MAIL_SRV_CONF": "ngxMailSrvConf", + "NGX_CONF_FLAG": "ngxConfFlag", + "NGX_HTTP_SRV_CONF": "ngxHTTPSrvConf", + "NGX_CONF_1MORE": "ngxConf1More", + "NGX_ANY_CONF": "ngxAnyConf", + "NGX_CONF_TAKE123": "ngxConfTake123", + "NGX_MAIN_CONF": "ngxMainConf", + "NGX_CONF_NOARGS": "ngxConfNoArgs", + "NGX_CONF_2MORE": "ngxConf2More", + "NGX_CONF_TAKE3": "ngxConfTake3", + "NGX_HTTP_SIF_CONF": "ngxHTTPSifConf", + "NGX_EVENT_CONF": "ngxEventConf", + "NGX_CONF_BLOCK": "ngxConfBlock", + "NGX_HTTP_LOC_CONF": "ngxHTTPLocConf", + "NGX_STREAM_SRV_CONF": "ngxStreamSrvConf", + "NGX_DIRECT_CONF": "ngxDirectConf", + "NGX_CONF_TAKE13": "ngxConfTake13", + "NGX_CONF_ANY": "ngxConfAny", + "NGX_CONF_TAKE4": "ngxConfTake4", + "NGX_CONF_TAKE5": "ngxConfTake5", + "NGX_CONF_TAKE6": "ngxConfTake6", + "NGX_CONF_TAKE7": "ngxConfTake7", +} + +//nolint:nonamedreturns +func masksFromFile(path string) (directive2Masks map[string][]mask, err error) { + directive2Masks = make(map[string][]mask, 0) + byteContent, err := os.ReadFile(path) + if err != nil { + return nil, err + } + strContent := string(byteContent) + + // Remove comments + strContent = singleLineCommentExtracter.ReplaceAllString(strContent, "") + strContent = multiLineCommentExtracter.ReplaceAllString(strContent, "") + strContent = strings.ReplaceAll(strContent, "\r\n", "") + strContent = strings.ReplaceAll(strContent, "\n", "") + + // Extract directives definition code blocks, each code block contains a list of directives definition + blocks := directivesDefineBlockExtracter.FindAllStringSubmatch(strContent, -1) + + for _, block := range blocks { + // Extract directives and their attributes in the code block, the first dimension of subBlocks + // is index of directive, the second dimension is index of attributes + subBlocks := singleDirectiveExtracter.FindAllStringSubmatch(block[2], -1) + + // Iterate through every directive + for _, attributes := range subBlocks { + // Extract attributes from the directive + directiveName := strings.TrimSpace(attributes[1]) + directiveMask := strings.Split(attributes[2], "|") + + // Transfer C-style mask to go style + for idx, ngxVarName := range directiveMask { + goVarName, found := ngxVarNameToGo[strings.TrimSpace(ngxVarName)] + if !found { + return nil, fmt.Errorf("parsing directive %s, bitmask %s in source code not found in crossplane", directiveName, ngxVarName) + } + directiveMask[idx] = goVarName + } + + directive2Masks[directiveName] = append(directive2Masks[directiveName], directiveMask) + } + } + return directive2Masks, nil +} + +//nolint:nonamedreturns +func getMasksFromPath(path string) (directive2Masks map[string][]mask, err error) { + directive2Masks = make(map[string][]mask, 0) + + err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + // Check if the entry is a C/C++ file + // Some dynamic modules are written in C++, like otel + if d.IsDir() { + return nil + } + + if !(strings.HasSuffix(path, ".c") || strings.HasSuffix(path, ".cpp")) { + return nil + } + + directive2MasksInFile, err := masksFromFile(path) + if err != nil { + return err + } + + for directive, masksInFile := range directive2MasksInFile { + directive2Masks[directive] = append(directive2Masks[directive], masksInFile...) + } + + return nil + }) + + if err != nil { + return nil, err + } + + if len(directive2Masks) == 0 { + return nil, errors.New("can't find any directives in the directory and subdirectories, please check the path") + } + + return directive2Masks, nil +} + +func genFromSrcCode(codePath string, mapVariableName string, matchFnName string, writer io.Writer) error { + directive2Masks, err := getMasksFromPath(codePath) + if err != nil { + return err + } + + err = supportFileTmpl.Execute(writer, supportFileTmplStruct{ + Directive2Masks: directive2Masks, + MapVariableName: mapVariableName, + MatchFnName: matchFnName, + }) + if err != nil { + return err + } + + return nil +} diff --git a/internal/generator/generator_util_test.go b/internal/generator/generator_util_test.go new file mode 100644 index 00000000..ebdaad3e --- /dev/null +++ b/internal/generator/generator_util_test.go @@ -0,0 +1,175 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +package generator + +import ( + "bytes" + "errors" + "flag" + "io" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +//nolint:gochecknoglobals +var ( + update = flag.Bool("update", false, + `update the expected output of these tests, +only use when the expected output is outdated and you are sure your output is correct`) +) + +func TestMain(m *testing.M) { + flag.Parse() + os.Exit(m.Run()) +} + +func getProjectRootAbsPath() (string, error) { + _, filePath, _, ok := runtime.Caller(0) + if !ok { + return "", errors.New("can't get path of generator_util_test.go through runtime") + } + absPath, err := filepath.Abs(filePath) + if err != nil { + return "", err + } + // get the project root directory + rootDir := filepath.Dir(absPath) + rootDir = filepath.Dir(rootDir) + rootDir = filepath.Dir(rootDir) + + return rootDir, nil +} + +func getTestSrcCodePath(relativePath string) (string, error) { + root, err := getProjectRootAbsPath() + if err != nil { + return "", err + } + return path.Join(root, "internal", "generator", "testdata", "source_codes", relativePath), nil +} + +func getExpectedFilePath(relativePath string) (string, error) { + root, err := getProjectRootAbsPath() + if err != nil { + return "", err + } + relativePath = strings.TrimSuffix(relativePath, ".c") + relativePath = strings.TrimSuffix(relativePath, ".cpp") + return path.Join(root, "internal", "generator", "testdata", "expected", relativePath), nil +} + +//nolint:funlen,gocognit +func TestGenSupFromSrcCode(t *testing.T) { + t.Parallel() + tests := []struct { + name string + relativePath string + wantErr bool + }{ + { + name: "normalDirectiveDefinition_pass", + relativePath: "normalDefinition", + wantErr: false, + }, + { + name: "unknownBitmask_fail", + relativePath: "unknownBitmask", + wantErr: true, + }, + { + name: "noDirectivesDefinition_fail", + relativePath: "noDirectives", + wantErr: true, + }, + // If one directive was defined in several files, we should keep all + // of the bitmask definitions + { + name: "directiveRepeatDefine_pass", + relativePath: "repeatDefine", + }, + // If there are comments in definition, we should delete them + { + name: "commentsInDefinition_pass", + relativePath: "commentsInDefinition", + }, + // If there are comments in definition, we should delete them + { + name: "genFromSingleFile_pass", + relativePath: "single_file.c", + }, + { + name: "fullNgxBitmaskCover_pass", + relativePath: "fullNgxBitmaskCover", + }, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + var err error + codePath, err := getTestSrcCodePath(tc.relativePath) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + err = genFromSrcCode(codePath, "directives", "Match", &buf) + + if !tc.wantErr && err != nil { + t.Fatal(err) + } + + if tc.wantErr && err == nil { + t.Fatal("expected error, got nil") + } + + // If the testcase wants an error and there is an error, skip the output file validation, + // since there may not be an output file while error occurs in generation. + if err != nil { + return + } + + expectedFilePth, err := getExpectedFilePath(tc.relativePath) + if err != nil { + t.Fatal(err) + } + + expectedFile, err := os.OpenFile(expectedFilePth, os.O_RDWR|os.O_CREATE, 0644) + if err != nil { + t.Fatal(err) + } + + defer func() { + if err = expectedFile.Close(); err != nil { + t.Fatal(err) + } + }() + + if *update { + _, err = expectedFile.WriteString(buf.String()) + if err != nil { + t.Fatal(err) + } + return + } + + expected, err := io.ReadAll(expectedFile) + if err != nil { + t.Fatal(err) + } + require.Equal(t, string(expected), buf.String()) + }) + } +} diff --git a/internal/generator/testdata/expected/commentsInDefinition b/internal/generator/testdata/expected/commentsInDefinition new file mode 100644 index 00000000..c7c56eaa --- /dev/null +++ b/internal/generator/testdata/expected/commentsInDefinition @@ -0,0 +1,35 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package crossplane + +var directives = map[string][]uint{ + "my_directive_1": { + ngxHTTPMainConf | ngxConfTake2, + }, + "my_directive_2": { + ngxHTTPMainConf | ngxConfFlag, + }, + "my_directive_3": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxConfNoArgs, + }, +} + +// Match is a matchFunc for parsing an NGINX config that contains the +// preceding directives. +func Match(directive string) ([]uint, bool) { + m, ok := directives[directive] + return m, ok +} \ No newline at end of file diff --git a/internal/generator/testdata/expected/fullNgxBitmaskCover b/internal/generator/testdata/expected/fullNgxBitmaskCover new file mode 100644 index 00000000..52f5686a --- /dev/null +++ b/internal/generator/testdata/expected/fullNgxBitmaskCover @@ -0,0 +1,29 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package crossplane + +var directives = map[string][]uint{ + "all_bitmask": { + ngxConfNoArgs | ngxConfTake1 | ngxConfTake2 | ngxConfTake3 | ngxConfTake4 | ngxConfTake5 | ngxConfTake6 | ngxConfTake7 | ngxConfTake12 | ngxConfTake13 | ngxConfTake23 | ngxConfTake123 | ngxConfTake1234 | ngxConfBlock | ngxConfFlag | ngxConfAny | ngxConf1More | ngxConf2More | ngxDirectConf | ngxMainConf, + }, +} + +// Match is a matchFunc for parsing an NGINX config that contains the +// preceding directives. +func Match(directive string) ([]uint, bool) { + m, ok := directives[directive] + return m, ok +} \ No newline at end of file diff --git a/internal/generator/testdata/expected/normalDefinition b/internal/generator/testdata/expected/normalDefinition new file mode 100644 index 00000000..c7c56eaa --- /dev/null +++ b/internal/generator/testdata/expected/normalDefinition @@ -0,0 +1,35 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package crossplane + +var directives = map[string][]uint{ + "my_directive_1": { + ngxHTTPMainConf | ngxConfTake2, + }, + "my_directive_2": { + ngxHTTPMainConf | ngxConfFlag, + }, + "my_directive_3": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxConfNoArgs, + }, +} + +// Match is a matchFunc for parsing an NGINX config that contains the +// preceding directives. +func Match(directive string) ([]uint, bool) { + m, ok := directives[directive] + return m, ok +} \ No newline at end of file diff --git a/internal/generator/testdata/expected/repeatDefine b/internal/generator/testdata/expected/repeatDefine new file mode 100644 index 00000000..a8909503 --- /dev/null +++ b/internal/generator/testdata/expected/repeatDefine @@ -0,0 +1,38 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package crossplane + +var directives = map[string][]uint{ + "my_directive_1": { + ngxHTTPMainConf | ngxConfTake2, + ngxHTTPMainConf | ngxConfTake1, + }, + "my_directive_2": { + ngxHTTPMainConf | ngxConfFlag, + ngxHTTPMainConf | ngxConfNoArgs, + }, + "my_directive_3": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxConfNoArgs, + ngxHTTPMainConf | ngxConfTake2, + }, +} + +// Match is a matchFunc for parsing an NGINX config that contains the +// preceding directives. +func Match(directive string) ([]uint, bool) { + m, ok := directives[directive] + return m, ok +} \ No newline at end of file diff --git a/internal/generator/testdata/expected/single_file b/internal/generator/testdata/expected/single_file new file mode 100644 index 00000000..6442f963 --- /dev/null +++ b/internal/generator/testdata/expected/single_file @@ -0,0 +1,32 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package crossplane + +var directives = map[string][]uint{ + "my_directive_1": { + ngxHTTPMainConf | ngxConfTake2, + }, + "my_directive_2": { + ngxHTTPMainConf | ngxConfFlag, + }, +} + +// Match is a matchFunc for parsing an NGINX config that contains the +// preceding directives. +func Match(directive string) ([]uint, bool) { + m, ok := directives[directive] + return m, ok +} \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/commentsInDefinition/with_comments.c b/internal/generator/testdata/source_codes/commentsInDefinition/with_comments.c new file mode 100644 index 00000000..9bc2bfff --- /dev/null +++ b/internal/generator/testdata/source_codes/commentsInDefinition/with_comments.c @@ -0,0 +1,23 @@ +static ngx_command_t my_directives[] = { + + { ngx_string("my_directive_1"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2/*igonre me*/, + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_2"/*igonre me*/), + NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_3"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_NOARGS, + 0, + 0, + 0, + NULL }, + + ngx_null_command +}; \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/fullNgxBitmaskCover/all_bitmask.c b/internal/generator/testdata/source_codes/fullNgxBitmaskCover/all_bitmask.c new file mode 100644 index 00000000..add2b16e --- /dev/null +++ b/internal/generator/testdata/source_codes/fullNgxBitmaskCover/all_bitmask.c @@ -0,0 +1,15 @@ +static ngx_command_t my_directives[] = { + + { ngx_string("all_bitmask"), + NGX_CONF_NOARGS|NGX_CONF_TAKE1| + NGX_CONF_TAKE2|NGX_CONF_TAKE3|NGX_CONF_TAKE4|NGX_CONF_TAKE5|NGX_CONF_TAKE6|NGX_CONF_TAKE7| + NGX_CONF_TAKE12|NGX_CONF_TAKE13|NGX_CONF_TAKE23|NGX_CONF_TAKE123|NGX_CONF_TAKE1234| + NGX_CONF_BLOCK|NGX_CONF_FLAG|NGX_CONF_ANY|NGX_CONF_1MORE| + NGX_CONF_2MORE|NGX_DIRECT_CONF|NGX_MAIN_CONF + , + 0, + 0, + NULL }, + + ngx_null_command +}; \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/noDirectives/noDirectives.c b/internal/generator/testdata/source_codes/noDirectives/noDirectives.c new file mode 100644 index 00000000..5c28f858 --- /dev/null +++ b/internal/generator/testdata/source_codes/noDirectives/noDirectives.c @@ -0,0 +1 @@ +// nothing here \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/normalDefinition/normal.c b/internal/generator/testdata/source_codes/normalDefinition/normal.c new file mode 100644 index 00000000..54f827e6 --- /dev/null +++ b/internal/generator/testdata/source_codes/normalDefinition/normal.c @@ -0,0 +1,23 @@ +static ngx_command_t my_directives[] = { + + { ngx_string("my_directive_1"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_2"), + NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_3"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_NOARGS, + 0, + 0, + 0, + NULL }, + + ngx_null_command +}; \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/repeatDefine/definition1.c b/internal/generator/testdata/source_codes/repeatDefine/definition1.c new file mode 100644 index 00000000..ec349116 --- /dev/null +++ b/internal/generator/testdata/source_codes/repeatDefine/definition1.c @@ -0,0 +1,23 @@ +static ngx_command_t my_directives[] = { + + { ngx_string("my_directive_1"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, + 0, + 0, + 0, + NULL }, /*comments will be igored by generator*/ + { ngx_string("my_directive_2"), + NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,/*comments will be igored by generator*/ + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_3"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_NOARGS,//comments will be igored by generator + 0, + 0, + 0, + NULL }, + + ngx_null_command +}; \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/repeatDefine/definition2.c b/internal/generator/testdata/source_codes/repeatDefine/definition2.c new file mode 100644 index 00000000..52397de9 --- /dev/null +++ b/internal/generator/testdata/source_codes/repeatDefine/definition2.c @@ -0,0 +1,23 @@ +static ngx_command_t my_directives[] = { + + { ngx_string("my_directive_1"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + 0, + 0, + 0, + NULL }, /*comments will be igored by generator*/ + { ngx_string("my_directive_2"), + NGX_HTTP_MAIN_CONF|NGX_CONF_NOARGS,/*comments will be igored by generator*/ + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_3"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,//comments will be igored by generator + 0, + 0, + 0, + NULL }, + + ngx_null_command +}; \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/single_file.c b/internal/generator/testdata/source_codes/single_file.c new file mode 100644 index 00000000..ebebe7a9 --- /dev/null +++ b/internal/generator/testdata/source_codes/single_file.c @@ -0,0 +1,17 @@ +static ngx_command_t my_directives[] = { + + { ngx_string("my_directive_1"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_2"), + NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, + 0, + 0, + 0, + NULL }, + + ngx_null_command +}; \ No newline at end of file diff --git a/internal/generator/testdata/source_codes/unknownBitmask/unknownBitmask.c b/internal/generator/testdata/source_codes/unknownBitmask/unknownBitmask.c new file mode 100644 index 00000000..827b97e1 --- /dev/null +++ b/internal/generator/testdata/source_codes/unknownBitmask/unknownBitmask.c @@ -0,0 +1,23 @@ +static ngx_command_t my_directives[] = { + + { ngx_string("my_directive_1"), + NGX_HTTP_MAIN_CONF|FAKE_BITMASK, // unknown bitmask FAKE_BITMASK + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_2"), + NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, + 0, + 0, + 0, + NULL }, + { ngx_string("my_directive_3"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_NOARGS, + 0, + 0, + 0, + NULL }, + + ngx_null_command +}; \ No newline at end of file diff --git a/internal/generator/tmpl/support_file.tmpl b/internal/generator/tmpl/support_file.tmpl new file mode 100644 index 00000000..dd61d732 --- /dev/null +++ b/internal/generator/tmpl/support_file.tmpl @@ -0,0 +1,33 @@ +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package crossplane + +var {{.MapVariableName}} = map[string][]uint{ +{{- range $name, $bitDefs := .Directive2Masks}} + "{{$name}}": {{"{"}} + {{- range $bitDef := $bitDefs}} + {{Join $bitDef " | "}}, + {{- end}} + {{"}"}}, +{{- end}} +} + +// {{.MatchFnName}} is a matchFunc for parsing an NGINX config that contains the +// preceding directives. +func {{.MatchFnName}}(directive string) ([]uint, bool) { + m, ok := {{.MapVariableName}}[directive] + return m, ok +} \ No newline at end of file diff --git a/vendor/golang.org/x/sys/execabs/execabs_go118.go b/vendor/golang.org/x/sys/execabs/execabs_go118.go index 2000064a..5627d70e 100644 --- a/vendor/golang.org/x/sys/execabs/execabs_go118.go +++ b/vendor/golang.org/x/sys/execabs/execabs_go118.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.19 -// +build !go1.19 package execabs diff --git a/vendor/golang.org/x/sys/execabs/execabs_go119.go b/vendor/golang.org/x/sys/execabs/execabs_go119.go index f364b341..d60ab1b4 100644 --- a/vendor/golang.org/x/sys/execabs/execabs_go119.go +++ b/vendor/golang.org/x/sys/execabs/execabs_go119.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.19 -// +build go1.19 package execabs diff --git a/vendor/modules.txt b/vendor/modules.txt index 3e04078c..5473f808 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -26,8 +26,10 @@ github.com/stretchr/testify/require golang.org/x/mod/internal/lazyregexp golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/sys v0.7.0 -## explicit; go 1.17 +# golang.org/x/net v0.24.0 +## explicit; go 1.18 +# golang.org/x/sys v0.19.0 +## explicit; go 1.18 golang.org/x/sys/execabs # golang.org/x/tools v0.8.0 ## explicit; go 1.18