diff --git a/define/build.go b/define/build.go index 1a452067780..955ac12cb40 100644 --- a/define/build.go +++ b/define/build.go @@ -274,6 +274,8 @@ type BuildOptions struct { CommonBuildOpts *CommonBuildOptions // CPPFlags are additional arguments to pass to the C Preprocessor (cpp). CPPFlags []string + // Preprocess tells the builder to run the C Preprocessor (cpp) regardless of the file extension. + Preprocess types.OptionalBool // DefaultMountsFilePath is the file path holding the mounts to be mounted for RUN // instructions in "host-path:container-path" format DefaultMountsFilePath string diff --git a/docs/buildah-build.1.md b/docs/buildah-build.1.md index 91892d72bd4..63f64051479 100644 --- a/docs/buildah-build.1.md +++ b/docs/buildah-build.1.md @@ -18,7 +18,7 @@ The build context directory can be specified as the http(s) URL of an archive, g If no context directory is specified, then Buildah will assume the current working directory as build context, which should contain a Containerfile. -Containerfiles ending with a ".in" suffix will be preprocessed via cpp(1). This can be useful to decompose Containerfiles into several reusable parts that can be used via CPP's **#include** directive. Notice, a Containerfile.in file can still be used by other tools when manually preprocessing them via `cpp -E`. Any comments ( Lines beginning with `#` ) in included Containerfile(s) that are not preprocess commands, will be printed as warnings during builds. +Containerfiles ending with a ".in" suffix will be automatically preprocessed via cpp(1). This can be useful to decompose Containerfiles into several reusable parts that can be used via CPP's **#include** directive. Notice, a Containerfile.in file can still be used by other tools when manually preprocessing them via `cpp -E`. Any comments ( Lines beginning with `#` ) in included Containerfile(s) that are not preprocess commands, will be printed as warnings during builds. When the URL is an archive, the contents of the URL is downloaded to a temporary location and extracted before execution. @@ -829,6 +829,13 @@ The `buildah build` command allows building images for all Linux architectures, **NOTE:** The `--platform` option may not be used in combination with the `--arch`, `--os`, or `--variant` options. +**--preprocess** + +If specified, will always attempt to use the C Preprocessor cpp(1), +even if the Containerfile doesn't end with the ".in" suffix. +Note: You can also configure this behavior by setting the BUILDAH\_PREPROCESS +environment variable to `1`, `true`, or `yes`. + **--pull** Pull image policy. If not specified, the default is **missing**. If an explicit diff --git a/imagebuildah/build.go b/imagebuildah/build.go index 00cf2be9bde..b7ea843bf7d 100644 --- a/imagebuildah/build.go +++ b/imagebuildah/build.go @@ -162,8 +162,10 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B data = contents } - // pre-process Dockerfiles with ".in" suffix - if strings.HasSuffix(dfile, ".in") { + // pre-process Dockerfiles with ".in" suffix, if --preprocess is specified, or if BUILDAH_PREPROCESS is set + preprocessSpecified := options.Preprocess == types.OptionalBoolTrue + preprocessInferred := (options.Preprocess == types.OptionalBoolUndefined) && strings.HasSuffix(dfile, ".in") + if preprocessSpecified || preprocessInferred { pData, err := preprocessContainerfileContents(logger, dfile, data, options.ContextDirectory, options.CPPFlags) if err != nil { return "", nil, err diff --git a/pkg/cli/build.go b/pkg/cli/build.go index 886812f16eb..8ff9ce52fd7 100644 --- a/pkg/cli/build.go +++ b/pkg/cli/build.go @@ -399,7 +399,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( sbomScanOptions = append(sbomScanOptions, *sbomScanOption) } - var compatVolumes, createdAnnotation, inheritAnnotations, inheritLabels, skipUnusedStages types.OptionalBool + var compatVolumes, createdAnnotation, inheritAnnotations, inheritLabels, skipUnusedStages, preprocess types.OptionalBool if c.Flag("compat-volumes").Changed { compatVolumes = types.NewOptionalBool(iopts.CompatVolumes) } @@ -415,6 +415,9 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( if c.Flag("skip-unused-stages").Changed { skipUnusedStages = types.NewOptionalBool(iopts.SkipUnusedStages) } + if c.Flag("preprocess").Changed || DefaultPreprocess() { + preprocess = types.NewOptionalBool(iopts.Preprocess) + } options = define.BuildOptions{ AddCapabilities: iopts.CapAdd, @@ -433,6 +436,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( CompatVolumes: compatVolumes, ConfidentialWorkload: confidentialWorkloadOptions, CPPFlags: iopts.CPPFlags, + Preprocess: preprocess, CommonBuildOpts: commonOpts, Compression: compression, CompressionFormat: compressionFormat, diff --git a/pkg/cli/common.go b/pkg/cli/common.go index 7d283c57cdc..d10e3deb535 100644 --- a/pkg/cli/common.go +++ b/pkg/cli/common.go @@ -95,6 +95,7 @@ type BudResults struct { Timestamp int64 OmitHistory bool OCIHooksDir []string + Preprocess bool Pull string PullAlways bool PullNever bool @@ -250,6 +251,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { fs.BoolVar(&flags.InheritLabels, "inherit-labels", true, "inherit the labels from the base image or base stages.") fs.BoolVar(&flags.InheritAnnotations, "inherit-annotations", true, "inherit the annotations from the base image or base stages.") fs.StringArrayVar(&flags.CPPFlags, "cpp-flag", []string{}, "set additional flag to pass to C preprocessor (cpp)") + fs.BoolVar(&flags.Preprocess, "preprocess", DefaultPreprocess(), "use the C preprocessor (cpp) on a Dockerfile, regardless of the file suffix. Use BUILDAH_PREPROCESS environment variable to change default.") fs.BoolVar(&flags.CreatedAnnotation, "created-annotation", true, `set an "org.opencontainers.image.created" annotation in the image`) fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry") fs.StringVarP(&flags.CWOptions, "cw", "", "", "confidential workload `options`") @@ -544,6 +546,16 @@ func DefaultHistory() bool { return false } +// DefaultPreprocess returns true if BUILDAH_PREPROCESS is set to "1" or "true" +// otherwise it returns false +func DefaultPreprocess() bool { + preprocess := os.Getenv("BUILDAH_PREPROCESS") + if strings.ToLower(preprocess) == "true" || strings.ToLower(preprocess) == "yes" || preprocess == "1" { + return true + } + return false +} + func VerifyFlagsArgsOrder(args []string) error { for _, arg := range args { if strings.HasPrefix(arg, "-") { diff --git a/tests/bud.bats b/tests/bud.bats index 83173c9ca66..0ae6652f131 100644 --- a/tests/bud.bats +++ b/tests/bud.bats @@ -4126,6 +4126,66 @@ _EOF expect_output --substring "Ignoring :5:2: error: #error" } +@test "bud with preprocessor, via --preprocess" { + _prefetch busybox + target=alpine-image + run_buildah build $WITH_POLICY_JSON -t ${target} --preprocess -f Decomposed.tpl $BUDFILES/preprocess +} + +@test "bud with preprocessor-requiring containerfile, without --preprocess" { + _prefetch busybox + target=alpine-image + run_buildah 125 build $WITH_POLICY_JSON -t ${target} -f Decomposed.tpl $BUDFILES/preprocess + expect_output --substring 'Build error: Unknown instruction: "RUNHELLO"' +} + +@test "bud with preprocessor error, via --preprocess" { + _prefetch busybox + target=alpine-image + run_buildah bud $WITH_POLICY_JSON -t ${target} --preprocess -f Error.tpl $BUDFILES/preprocess + expect_output --substring "Ignoring :5:2: error: #error" +} + +@test "bud with preprocessor, via env var 1" { + _prefetch busybox + target=alpine-image + BUILDAH_PREPROCESS=1 run_buildah build $WITH_POLICY_JSON -t ${target} -f Decomposed.tpl $BUDFILES/preprocess +} + +@test "bud with preprocessor, via env var true" { + _prefetch busybox + target=alpine-image + BUILDAH_PREPROCESS=true run_buildah build $WITH_POLICY_JSON -t ${target} -f Decomposed.tpl $BUDFILES/preprocess +} + +@test "bud with preprocessor, via env var yes" { + _prefetch busybox + target=alpine-image + BUILDAH_PREPROCESS=yes run_buildah build $WITH_POLICY_JSON -t ${target} -f Decomposed.tpl $BUDFILES/preprocess +} + +@test "bud with preprocessor error, via env var" { + _prefetch busybox + target=alpine-image + BUILDAH_PREPROCESS=1 run_buildah bud $WITH_POLICY_JSON -t ${target} -f Error.tpl $BUDFILES/preprocess + expect_output --substring "Ignoring :5:2: error: #error" +} + +@test "bud with preprocessor-requiring containerfile.in, with preprocess force disabled" { + _prefetch busybox + target=alpine-image + starthttpd $BUDFILES/preprocess + run_buildah 125 build $WITH_POLICY_JSON -t ${target} --preprocess=false -f Decomposed.in $BUDFILES/preprocess + expect_output --substring 'Build error: Unknown instruction: "RUNHELLO"' +} + +@test "bud with preprocessor-requiring containerfile, via explicit cli flag, defaulted on via env var" { + _prefetch busybox + target=alpine-image + BUILDAH_PREPROCESS=1 run_buildah 125 build $WITH_POLICY_JSON -t ${target} --preprocess=false -f Decomposed.tpl $BUDFILES/preprocess + expect_output --substring 'Build error: Unknown instruction: "RUNHELLO"' +} + @test "bud-with-rejected-name" { target=ThisNameShouldBeRejected run_buildah 125 build -q $WITH_POLICY_JSON -t ${target} $BUDFILES/from-scratch diff --git a/tests/bud/preprocess/Decomposed.tpl b/tests/bud/preprocess/Decomposed.tpl new file mode 100644 index 00000000000..2cb796118d6 --- /dev/null +++ b/tests/bud/preprocess/Decomposed.tpl @@ -0,0 +1,5 @@ +FROM busybox + +#include "common" + +RUNHELLO diff --git a/tests/bud/preprocess/Error.tpl b/tests/bud/preprocess/Error.tpl new file mode 100644 index 00000000000..b95fcaad169 --- /dev/null +++ b/tests/bud/preprocess/Error.tpl @@ -0,0 +1,5 @@ +FROM busybox + +#include "common" + +#error THISERROR