diff --git a/example/fundamentals/tasks/4-inputs/build.mill b/example/fundamentals/tasks/4-inputs/build.mill index 01efc400aa8..307b0206a00 100644 --- a/example/fundamentals/tasks/4-inputs/build.mill +++ b/example/fundamentals/tasks/4-inputs/build.mill @@ -79,3 +79,64 @@ def gitStatusTask2 = Task { "version-" + gitStatusInput() } // code you put in `Task.Input` runs quickly. Ideally it should just be a simple check // "did anything change?" and any heavy-lifting should be delegated to downstream // tasks where it can be cached if possible. +// +// === System Properties Inputs +// +// One major use case of `Input` tasks is to make your build configurable via +// JVM system properties of environment variables. If you directly access +// `sys.props` or `sys.env` inside a xref:#_cached_tasks[cached Task{}], the +// cached value will be used even if the property or environment variable changes +// in subsequent runs, when you really want it to be re-evaluated. Thus, accessing +// system properties should be done in a `Task.Input`, and usage of the property +// should be done downstream in a xref:#_cached_tasks[cached task]: + +def myPropertyInput = Task.Input { + sys.props("my-property") +} +def myPropertyTask = Task { + "Hello Prop " + myPropertyInput() +} + +/** Usage + +> ./mill show myPropertyTask +"Hello Prop null" + +> ./mill -Dmy-property=world show myPropertyTask # Task is correctly invalidated when prop is added +"Hello Prop world" + +> ./mill show myPropertyTask # Task is correctly invalidated when prop is removed +"Hello Prop null" +*/ + +// Again, `Task.Input` runs every time, and thus you should only do the bare minimum +// in your `Task.Input` that is necessary to detect changes. Any further processing +// should be done in downstreak xref:#_cached_tasks[cached tasks] to allow for proper +// caching and re-use +// +// === Environment Variable Inputs +// +// Like system properties, environment variables should be referenced in `Task.Input`s. Unlike +// system properties, you need to use the special API `Task.env` to access the environment, +// due to JVM limitations: + +def myEnvInput = Task.Input { + Task.env.getOrElse("MY_ENV", null) +} + +def myEnvTask = Task { + "Hello Env " + myEnvInput() +} + + +/** Usage + +> ./mill show myEnvTask +"Hello Env null" + +> MY_ENV=world ./mill show myEnvTask # Task is correctly invalidated when env is added +"Hello Env world" + +> ./mill show myEnvTask # Task is correctly invalidated when env is removed +"Hello Env null" +*/ diff --git a/runner/src/mill/runner/MillMain.scala b/runner/src/mill/runner/MillMain.scala index a43f6a07893..3f42d60247c 100644 --- a/runner/src/mill/runner/MillMain.scala +++ b/runner/src/mill/runner/MillMain.scala @@ -414,7 +414,7 @@ object MillMain { ): Unit = { val currentProps = sys.props val desiredProps = initialSystemProperties ++ userSpecifiedProperties - val systemPropertiesToUnset = desiredProps.keySet -- currentProps.keySet + val systemPropertiesToUnset = currentProps.keySet -- desiredProps.keySet for (k <- systemPropertiesToUnset) System.clearProperty(k) for ((k, v) <- desiredProps) System.setProperty(k, v)