// PrepareBuild prepares a custombuild.Builder for generating a custom binary using // middlewares. A call to Build on the returned builder will generate the binary. // If error is not nil, the returned Builder should be ignored. func PrepareBuild(middlewares features.Middlewares) (custombuild.Builder, error) { // create builder builder, err := custombuild.NewUnready(CaddyPackage, gen(middlewares), middlewares.Packages()) if err != nil { return builder, err } // TODO make this configurable and enable only for dev builder.UseNetworkForAll(false) err = builder.Setup() if err != nil { // not useful, clear assets go builder.Teardown() return builder, err } // necessary to ensure import "github.com/mholt/caddy..." is referencing // this code. err = builder.SetImportPath(CaddyPackage) if err != nil { // not useful, clear assets go builder.Teardown() return builder, err } return builder, nil }
// PrepareBuild prepares a custombuild.Builder for generating a custom binary using // middlewares. A call to Build on the returned builder will generate the binary. // If error is not nil, the returned Builder should be ignored. func PrepareBuild(middlewares features.Middlewares, pullLatest bool) (custombuild.Builder, error) { // create builder builder, err := custombuild.NewUnready(CaddyPackage, gen(middlewares), middlewares.Packages()) if err != nil { return builder, err } builder.UseNetworkForAll(pullLatest) // if true, run go get -u for all dependencies before each build err = builder.Setup() if err != nil { // not useful, clear assets go builder.Teardown() return builder, err } // necessary to ensure import "github.com/mholt/caddy..." is referencing // this code. err = builder.SetImportPath(CaddyPackage) if err != nil { // not useful, clear assets go builder.Teardown() return builder, err } return builder, nil }
// gen is code generation function that insert custom directives at runtime. func gen(middlewares features.Middlewares) custombuild.CodeGenFunc { return func(src string, packages []string) (err error) { // prevent possible panic from assertions. defer func() { if recover() != nil { err = errParse } }() // if no middleware is added, no code generation needed. if len(middlewares) == 0 { return nil } fset := token.NewFileSet() file := filepath.Join(src, directivesFile) f, err := parser.ParseFile(fset, file, nil, 0) if err != nil { return err } packageNames, err := getPackageNames(middlewares.Packages()) if err != nil { return err } for _, m := range middlewares { astutil.AddImport(fset, f, m.Package) } var buf bytes.Buffer err = printer.Fprint(&buf, fset, f) if err != nil { return err } out := buf.String() for _, mid := range middlewares { f, err = parser.ParseFile(token.NewFileSet(), "", out, 0) node, ok := f.Scope.Lookup("directiveOrder").Decl.(ast.Node) if !ok { return errParse } snippet := fmt.Sprintf(`{"%s", %s.Setup},`+"\n", mid.Directive, packageNames[mid.Package]) // add to end of directives. end := int(node.End()) - 2 // if after is set, locate directive and add after it. after := getPrevDirective(mid.Directive) if after != "" { found := false c := node.(*ast.ValueSpec).Values[0].(*ast.CompositeLit) for _, m := range c.Elts { directive := m.(*ast.CompositeLit).Elts[0].(*ast.BasicLit) if strconv.Quote(after) == directive.Value && !found { end = int(m.End()) + 1 found = true } // check if directive exists if strconv.Quote(mid.Directive) == directive.Value { return fmt.Errorf("Directive '%s' exists in Caddy core, use a distinct name.", mid.Directive) } } if !found { return fmt.Errorf("Cannot place afer %s, directive '%s' not found.", config.After, config.After) } } out = out[:end] + snippet + out[end:] } return ioutil.WriteFile(file, []byte(out), os.FileMode(0660)) } }