@@ -12,29 +12,51 @@ import (
1212
1313 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1414 "github.com/pterm/pterm"
15+ "github.com/spf13/cobra"
16+ "github.com/spf13/pflag"
17+ "oras.land/oras-go/pkg/content"
18+
1519 "github.com/solo-io/bumblebee/builder"
1620 "github.com/solo-io/bumblebee/pkg/cli/internal/options"
1721 "github.com/solo-io/bumblebee/pkg/internal/version"
1822 "github.com/solo-io/bumblebee/pkg/spec"
19- "github.com/spf13/cobra"
20- "github.com/spf13/pflag"
21- "oras.land/oras-go/pkg/content"
2223)
2324
2425type buildOptions struct {
25- BuildImage string
26- Builder string
27- OutputFile string
26+ BuildImage string
27+ Builder string
28+ OutputFile string
2829 Local bool
30+ BinaryOnly bool
31+ CFlags []string
32+ BuildScript string
33+ BuildScriptOutput bool
2934
3035 general * options.GeneralOptions
3136}
3237
38+ func (opts * buildOptions ) validate () error {
39+ if ! opts .Local {
40+ if opts .BuildScript != "" {
41+ fmt .Println ("ignoring specified build script for docker build" )
42+ }
43+ if opts .BuildScriptOutput {
44+ return fmt .Errorf ("cannot write build script output for docker build" )
45+ }
46+ }
47+
48+ return nil
49+ }
50+
3351func addToFlags (flags * pflag.FlagSet , opts * buildOptions ) {
3452 flags .StringVarP (& opts .BuildImage , "build-image" , "i" , fmt .Sprintf ("ghcr.io/solo-io/bumblebee/builder:%s" , version .Version ), "Build image to use when compiling BPF program" )
3553 flags .StringVarP (& opts .Builder , "builder" , "b" , "docker" , "Executable to use for docker build command, default: `docker`" )
3654 flags .StringVarP (& opts .OutputFile , "output-file" , "o" , "" , "Output file for BPF ELF. If left blank will default to <inputfile.o>" )
37- flags .BoolVarP (& opts .Local , "local" , "l" , false , "Build the output binary and OCI image using local tools" )
55+ flags .BoolVarP (& opts .Local , "local" , "l" , false , "Build the output binary using local tools" )
56+ flags .StringVar (& opts .BuildScript , "build-script" , "" , "Optional path to a build script for building BPF program locally" )
57+ flags .BoolVar (& opts .BuildScriptOutput , "build-script-out" , false , "Print local script bee will use to build the BPF program" )
58+ flags .BoolVar (& opts .BinaryOnly , "binary-only" , false , "Only create output binary and do not package it into an OCI image" )
59+ flags .StringArrayVar (& opts .CFlags , "cflags" , nil , "cflags to be used when compiling the BPF program, passed as environment variable 'CFLAGS'" )
3860}
3961
4062func Command (opts * options.GeneralOptions ) * cobra.Command {
@@ -52,8 +74,22 @@ The bee build command has 2 main parts
5274By default building is done in a docker container, however, this can be switched to local by adding the local flag:
5375$ build INPUT_FILE REGISTRY_REF --local
5476
77+ If you would prefer to build only the object file, you can include the '--binary-only' flag,
78+ in which case you do not need to specify a registry:
79+ $ build INPUT_FILE --binary-only
80+
81+ Examples:
82+ You can specify multiple cflags with either a space separated string, or with multiple instances:
83+ $ build INPUT_FILE REGISTRY_REF --cflags="-DDEBUG -DDEBUG2"
84+ $ build INPUT_FILE REGISTRY_REF --cflags=-DDEBUG --cflags=-DDEBUG2
85+
86+ You can specify your own build script for building the BPF program locally.
87+ the input file will be passed as argument '$1' and the output filename as '$2'.
88+ Use the '--build-script-out' flag to see the default build script bee uses:
89+ $ build INPUT_FILE REGISTRY_REF --local --build-script-out
90+ $ build INPUT_FILE REGISTRY_REF --local --build-script=build.sh
5591` ,
56- Args : cobra .ExactArgs ( 2 ),
92+ Args : cobra .RangeArgs ( 1 , 2 ),
5793 RunE : func (cmd * cobra.Command , args []string ) error {
5894 return build (cmd .Context (), args , buildOpts )
5995 },
@@ -69,6 +105,9 @@ $ build INPUT_FILE REGISTRY_REF --local
69105}
70106
71107func build (ctx context.Context , args []string , opts * buildOptions ) error {
108+ if err := opts .validate (); err != nil {
109+ return err
110+ }
72111
73112 inputFile := args [0 ]
74113 outputFile := opts .OutputFile
@@ -99,8 +138,17 @@ func build(ctx context.Context, args []string, opts *buildOptions) error {
99138 // Create and start a fork of the default spinner.
100139 var buildSpinner * pterm.SpinnerPrinter
101140 if opts .Local {
141+ buildScript , err := getBuildScript (opts .BuildScript )
142+ if err != nil {
143+ return fmt .Errorf ("could not load build script: %v" , err )
144+ }
145+ if opts .BuildScriptOutput {
146+ fmt .Printf ("%s\n " , buildScript )
147+ return nil
148+ }
149+
102150 buildSpinner , _ = pterm .DefaultSpinner .Start ("Compiling BPF program locally" )
103- if err := buildLocal (ctx , inputFile , outputFile ); err != nil {
151+ if err := buildLocal (ctx , opts , buildScript , inputFile , outputFile ); err != nil {
104152 buildSpinner .UpdateText ("Failed to compile BPF program locally" )
105153 buildSpinner .Fail ()
106154 return err
@@ -116,6 +164,14 @@ func build(ctx context.Context, args []string, opts *buildOptions) error {
116164 buildSpinner .UpdateText (fmt .Sprintf ("Successfully compiled \" %s\" and wrote it to \" %s\" " , inputFile , outputFile ))
117165 buildSpinner .Success () // Resolve spinner with success message.
118166
167+ if opts .BinaryOnly {
168+ return nil
169+ }
170+
171+ if len (args ) == 1 {
172+ return fmt .Errorf ("must specify a registry to package the output or run with '--binary-only'" )
173+ }
174+
119175 // TODO: Figure out this hack, file.Seek() didn't seem to work
120176 outputFd .Close ()
121177 reopened , err := os .Open (outputFile )
@@ -175,6 +231,18 @@ func getPlatformInfo(ctx context.Context) *ocispec.Platform {
175231 }
176232}
177233
234+ func getBuildScript (path string ) ([]byte , error ) {
235+ if path != "" {
236+ buildScript , err := os .ReadFile (path )
237+ if err != nil {
238+ return nil , fmt .Errorf ("could not read build script: %v" , err )
239+ }
240+ return buildScript , nil
241+ }
242+
243+ return builder .GetBuildScript (), nil
244+ }
245+
178246func buildDocker (
179247 ctx context.Context ,
180248 opts * buildOptions ,
@@ -190,10 +258,13 @@ func buildDocker(
190258 "run" ,
191259 "-v" ,
192260 fmt .Sprintf ("%s:/usr/src/bpf" , wd ),
193- opts .BuildImage ,
194- inputFile ,
195- outputFile ,
196261 }
262+
263+ if len (opts .CFlags ) > 0 {
264+ dockerArgs = append (dockerArgs , "--env" , fmt .Sprintf ("CFLAGS=%s" , strings .Join (opts .CFlags , " " )))
265+ }
266+ dockerArgs = append (dockerArgs , opts .BuildImage , inputFile , outputFile )
267+
197268 dockerCmd := exec .CommandContext (ctx , opts .Builder , dockerArgs ... )
198269 byt , err := dockerCmd .CombinedOutput ()
199270 if err != nil {
@@ -203,12 +274,19 @@ func buildDocker(
203274 return nil
204275}
205276
206- func buildLocal (ctx context.Context , inputFile , outputFile string ) error {
207- buildScript := builder .GetBuildScript ()
208-
277+ func buildLocal (
278+ ctx context.Context ,
279+ opts * buildOptions ,
280+ buildScript []byte ,
281+ inputFile ,
282+ outputFile string ,
283+ ) error {
209284 // Pass the script into sh via stdin, then arguments
210285 // TODO: need to handle CWD gracefully
211286 shCmd := exec .CommandContext (ctx , "sh" , "-s" , "--" , inputFile , outputFile )
287+ shCmd .Env = []string {
288+ fmt .Sprintf ("CFLAGS=%s" , strings .Join (opts .CFlags , " " )),
289+ }
212290 stdin , err := shCmd .StdinPipe ()
213291 if err != nil {
214292 return err
@@ -220,8 +298,8 @@ func buildLocal(ctx context.Context, inputFile, outputFile string) error {
220298 }()
221299
222300 out , err := shCmd .CombinedOutput ()
301+ pterm .Info .Printf ("%s\n " , out )
223302 if err != nil {
224- fmt .Printf ("%s\n " , out )
225303 return err
226304 }
227305 return nil
0 commit comments