Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99f18960 authored by LaMont Jones's avatar LaMont Jones
Browse files

Update SOONG_PARTIAL_COMPILE logic

Separate PARTIAL_COMPILE changes that affect analysis from actually
using it.

- SOONG_PARTIAL_COMPILE affects how we generate the Ninja file.  Changes
  to this cause reanalysis.

- SOONG_USE_PARTIAL_COMPILE is used in the rules to determine if we use
  the PARTIAL_COMPILE implementation, or the legacy rules.  This means
  that the developer can switch between full compiles and partial
  compiles at will without waiting for analysis to happen each time they
  change it.

Bug: b/365536323
Test: manual
Change-Id: Icb06687d17f63edb22bf2457a659b452537dadba
parent 130cbf6f
Loading
Loading
Loading
Loading
+104 −0
Original line number Diff line number Diff line
@@ -324,6 +324,9 @@ type config struct {
	AndroidCommonTarget      Target // the Target for common modules for the Android device
	AndroidFirstDeviceTarget Target // the first Target for modules for the Android device

	// Flags for Partial Compile, derived from SOONG_PARTIAL_COMPILE.
	partialCompileFlags partialCompileFlags

	// multilibConflicts for an ArchType is true if there is earlier configured
	// device architecture with the same multilib value.
	multilibConflicts map[ArchType]bool
@@ -373,6 +376,16 @@ type config struct {
	ensureAllowlistIntegrity bool
}

type partialCompileFlags struct {
	// Is partial compilation enabled at all?
	enabled bool

	// Whether to use d8 instead of r8
	use_d8 bool

	// Add others as needed.
}

type deviceConfig struct {
	config *config
	OncePer
@@ -382,6 +395,88 @@ type jsonConfigurable interface {
	SetDefaultConfig()
}

// Parse SOONG_PARTIAL_COMPILE.
//
// SOONG_PARTIAL_COMPILE determines which features are enabled or disabled in
// rule generation.  Changing this environment variable causes reanalysis.
//
// SOONG_USE_PARTIAL_COMPILE determines whether or not we **use** PARTIAL_COMPILE.
// Rule generation must support both cases, since changing it does not cause
// reanalysis.
//
// The user-facing documentation shows:
//
// - empty or not set: "The current default state"
// - "true" or "on": enable all stable partial compile features.
// - "false" or "off": disable partial compile completely.
//
// What we actually allow is a comma separated list of tokens, whose first
// character may be "+" (enable) or "-" (disable).  If neither is present, "+"
// is assumed.  For example, "on,+use_d8" will enable partial compilation, and
// additionally set the use_d8 flag (regardless of whether it is opt-in or
// opt-out).
//
// To add a new feature to the list, add the field in the struct
// `partialCompileFlags` above, and then add the name of the field in the
// switch statement below.
func (c *config) parsePartialCompileFlags() (partialCompileFlags, error) {
	defaultFlags := partialCompileFlags{
		// Set any opt-out flags here.  Opt-in flags are off by default.
		enabled: false,
	}
	value := c.Getenv("SOONG_PARTIAL_COMPILE")

	if value == "" {
		return defaultFlags, nil
	}

	ret := defaultFlags
	tokens := strings.Split(strings.ToLower(value), ",")
	makeVal := func(state string, defaultValue bool) bool {
		switch state {
		case "":
			return defaultValue
		case "-":
			return false
		case "+":
			return true
		}
		return false
	}
	for _, tok := range tokens {
		var state string
		if len(tok) == 0 {
			continue
		}
		switch tok[0:1] {
		case "":
			// Ignore empty tokens.
			continue
		case "-", "+":
			state = tok[0:1]
			tok = tok[1:]
		default:
			// Treat `feature` as `+feature`.
			state = "+"
		}
		switch tok {
		case "true":
			ret = defaultFlags
			ret.enabled = true
		case "false":
			// Set everything to false.
			ret = partialCompileFlags{}
		case "enabled":
			ret.enabled = makeVal(state, defaultFlags.enabled)
		case "use_d8":
			ret.use_d8 = makeVal(state, defaultFlags.use_d8)
		default:
			return partialCompileFlags{}, fmt.Errorf("Unknown SOONG_PARTIAL_COMPILE value: %v", value)
		}
	}
	return ret, nil
}

func loadConfig(config *config) error {
	return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
}
@@ -568,6 +663,11 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error)
		return Config{}, err
	}

	config.partialCompileFlags, err = config.parsePartialCompileFlags()
	if err != nil {
		return Config{}, err
	}

	// Make the CommonOS OsType available for all products.
	targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}

@@ -999,6 +1099,10 @@ func (c *config) DefaultAppTargetSdk(ctx EarlyModuleContext) ApiLevel {
	return ApiLevelOrPanic(ctx, codename)
}

func (c *config) PartialCompileFlags() partialCompileFlags {
	return c.partialCompileFlags
}

func (c *config) AppsDefaultVersionName() string {
	return String(c.productVariables.AppsDefaultVersionName)
}
+14 −90
Original line number Diff line number Diff line
@@ -98,7 +98,6 @@ type configImpl struct {
	buildFromSourceStub      bool
	incrementalBuildActions  bool
	ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
	partialCompileFlags      partialCompileFlags

	// From the product config
	katiArgs        []string
@@ -138,16 +137,6 @@ type configImpl struct {
	ninjaCommand ninjaCommandType
}

type partialCompileFlags struct {
	// Is partial compilation enabled at all?
	enabled bool

	// Whether to use d8 instead of r8
	use_d8 bool

	// Add others as needed.
}

type NinjaWeightListSource uint

const (
@@ -304,12 +293,24 @@ func NewConfig(ctx Context, args ...string) Config {
		ret.sandboxConfig.SetSrcDirIsRO(srcDirIsWritable == "false")
	}

	ret.partialCompileFlags = parsePartialCompileFlags(ctx)

	if os.Getenv("GENERATE_SOONG_DEBUG") == "true" {
		ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json"))
	}

	// If SOONG_USE_PARTIAL_COMPILE is set, make it one of "true" or the empty string.
	// This simplifies the generated Ninja rules, so that they only need to check for the empty string.
	if value, ok := os.LookupEnv("SOONG_USE_PARTIAL_COMPILE"); ok {
		if value == "true" || value == "1" || value == "y" || value == "yes" {
			value = "true"
		} else {
			value = ""
		}
		err = os.Setenv("SOONG_USE_PARTIAL_COMPILE", value)
		if err != nil {
			ctx.Fatalln("Failed to set SOONG_USE_PARTIAL_COMPILE: %v", err)
		}
	}

	ret.ninjaCommand = NINJA_NINJA
	switch os.Getenv("SOONG_NINJA") {
	case "n2":
@@ -382,7 +383,6 @@ func NewConfig(ctx Context, args ...string) Config {
		// Use config.ninjaCommand instead.
		"SOONG_NINJA",
		"SOONG_USE_N2",
		"SOONG_PARTIAL_COMPILE",
	)

	if ret.UseGoma() || ret.ForceUseGoma() {
@@ -501,78 +501,6 @@ func NewConfig(ctx Context, args ...string) Config {
	return c
}

// Parse SOONG_PARTIAL_COMPILE.
//
// The user-facing documentation shows:
//
// - empty or not set: "The current default state"
// - "true" or "on": enable all stable partial compile features.
// - "false" or "off": disable partial compile completely.
//
// What we actually allow is a comma separated list of tokens, whose first
// character may be "+" (enable) or "-" (disable).  If neither is present, "+"
// is assumed.  For example, "on,+use_d8" will enable partial compilation, and
// additionally set the use_d8 flag (regardless of whether it is opt-in or
// opt-out).
//
// To add a new feature to the list, add the field in the struct
// `partialCompileFlags` above, and then add the name of the field in the
// switch statement below.
func parsePartialCompileFlags(ctx Context) partialCompileFlags {
	defaultFlags := partialCompileFlags{
		// Set any opt-out flags here.  Opt-in flags are off by default.
		enabled: false,
	}
	value, ok := os.LookupEnv("SOONG_PARTIAL_COMPILE")

	if !ok {
		return defaultFlags
	}

	ret := defaultFlags
	tokens := strings.Split(strings.ToLower(value), ",")
	makeVal := func(state string, defaultValue bool) bool {
		switch state {
		case "":
			return defaultValue
		case "-":
			return false
		case "+":
			return true
		}
		return false
	}
	for _, tok := range tokens {
		var state string
		switch tok[0:1] {
		case "":
			// Ignore empty tokens.
			continue
		case "-", "+":
			state = tok[0:1]
			tok = tok[1:]
		default:
			// Treat `feature` as `+feature`.
			state = "+"
		}
		switch tok {
		case "true", "on", "yes":
			ret = defaultFlags
			ret.enabled = true
		case "false", "off", "no":
			// Set everything to false.
			ret = partialCompileFlags{}
		case "enabled":
			ret.enabled = makeVal(state, defaultFlags.enabled)
		case "use_d8":
			ret.use_d8 = makeVal(state, defaultFlags.use_d8)
		default:
			ctx.Fatalln("Unknown SOONG_PARTIAL_COMPILE value:", value)
		}
	}
	return ret
}

// NewBuildActionConfig returns a build configuration based on the build action. The arguments are
// processed based on the build action and extracts any arguments that belongs to the build action.
func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config {
@@ -1855,10 +1783,6 @@ func (c *configImpl) EnsureAllowlistIntegrity() bool {
	return c.ensureAllowlistIntegrity
}

func (c *configImpl) PartialCompileFlags() partialCompileFlags {
	return c.partialCompileFlags
}

// Returns a Time object if one was passed via a command-line flag.
// Otherwise returns the passed default.
func (c *configImpl) BuildStartedTimeOrDefault(defaultTime time.Time) time.Time {
+17 −0
Original line number Diff line number Diff line
@@ -183,6 +183,23 @@ func runKati(ctx Context, config Config, extraSuffix string, args []string, envF
		username = usernameFromEnv
	}

	// SOONG_USE_PARTIAL_COMPILE may be used in makefiles, but both cases must be supported.
	//
	// In general, the partial compile features will be implemented in Soong-based rules. We
	// also allow them to be used in makefiles.  Clear the environment variable when calling
	// kati so that we avoid reanalysis when the user changes it.  We will pass it to Ninja.
	// As a result, rules where we want to allow the developer to toggle the feature ("use
	// the partial compile feature" vs "legacy, aka full compile behavior") need to use this
	// in the rule, since changing it will not cause reanalysis.
	//
	// Shell syntax in the rule might look something like this:
	//     if [[ -n ${SOONG_USE_PARTIAL_COMPILE} ]]; then
	//         # partial compile behavior
	//     else
	//         # legacy behavior
	//     fi
	cmd.Environment.Unset("SOONG_USE_PARTIAL_COMPILE")

	hostname, ok := cmd.Environment.Get("BUILD_HOSTNAME")
	// Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
	cmd.Environment.Unset("BUILD_HOSTNAME")
+3 −0
Original line number Diff line number Diff line
@@ -241,6 +241,9 @@ func runNinjaForBuild(ctx Context, config Config) {
			"SOONG_USE_N2",
			"RUST_BACKTRACE",
			"RUST_LOG",

			// SOONG_USE_PARTIAL_COMPILE only determines which half of the rule we execute.
			"SOONG_USE_PARTIAL_COMPILE",
		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
	}