feat: lake: empty build detection & --allow-empty (#13500)

This PR adds a check for empty `lake build` invocations (as an empty
build usually indicates a misconfiguration). Builds with no jobs will
now print "Nothing to build." and invocations of `lake build` with no
default targets configured will produce a warning. This will be promoted
to an error in the future. The warning (and future error) can be
suppressed with the new `--allow-empty` CLI option.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mac Malone 2026-04-22 23:27:29 -04:00 committed by GitHub
parent 48c7a4f7d9
commit 30a3fde8aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 76 additions and 4 deletions

View file

@ -284,6 +284,9 @@ def reportResult (cfg : BuildConfig) (out : IO.FS.Stream) (result : MonitorResul
if result.failures.isEmpty then
if cfg.showProgress && cfg.showSuccess then
let numJobs := result.numJobs
if numJobs == 0 then
print! out "Nothing to build.\n"
else
let jobs := if numJobs == 1 then "1 job" else s!"{numJobs} jobs"
if cfg.noBuild then
print! out s!"All targets up-to-date ({jobs}).\n"

View file

@ -55,6 +55,7 @@ BASIC OPTIONS:
--packages=file JSON file of package entries that override the manifest
--reconfigure, -R elaborate configuration files instead of using OLeans
--keep-toolchain do not update toolchain on workspace update
--allow-empty accept bare builds with no default targets configured
--no-build exit immediately if a build target is not up-to-date
--no-cache build packages locally; do not download build caches
--try-cache attempt to download build caches for supported packages

View file

@ -56,6 +56,7 @@ public structure LakeOptions where
reconfigure : Bool := false
oldMode : Bool := false
trustHash : Bool := true
allowEmpty : Bool := false
noBuild : Bool := false
noCache : Option Bool := none
failLv : LogLevel := .error
@ -247,6 +248,7 @@ def lakeLongOption : (opt : String) → CliM PUnit
| "--old" => modifyThe LakeOptions ({· with oldMode := true})
| "--text" => modifyThe LakeOptions ({· with outFormat := .text})
| "--json" => modifyThe LakeOptions ({· with outFormat := .json})
| "--allow-empty" => modifyThe LakeOptions ({· with allowEmpty := true})
| "--no-build" => modifyThe LakeOptions ({· with noBuild := true})
| "--no-cache" => modifyThe LakeOptions ({· with noCache := true})
| "--try-cache" => modifyThe LakeOptions ({· with noCache := false})
@ -845,6 +847,12 @@ protected def build : CliM PUnit := do
let ws ← loadWorkspace config
let targetSpecs ← takeArgs
let specs ← parseTargetSpecs ws targetSpecs
if specs.isEmpty && !opts.allowEmpty then
logWarning "no targets specified and no default targets configured\
\n Note: This will be an error in a future version of Lake.\
\n Hint: This warning (or error) can be suppressed with '--allow-empty'."
if opts.failLv ≤ .warning then
exit 1
specs.forM fun spec =>
unless spec.buildable do
throw <| .invalidBuildTarget spec.info.key.toSimpleString

View file

@ -0,0 +1 @@
rm -rf .lake lake-manifest.json produced.out

View file

@ -0,0 +1,7 @@
import Lake
open Lake DSL
package test
lean_lib Lib where
globs := #[]

View file

@ -0,0 +1,5 @@
name = "test"
[[lean_lib]]
name = "Lib"
globs = []

View file

@ -0,0 +1,47 @@
#!/usr/bin/env bash
source ../common.sh
# This test covers the behavior of `lake build`
# with no default targets configured.
test_empty_build() {
cfg_file=$1
test_run -f $cfg_file update
test_out_diff <(cat << 'EOF'
warning: no targets specified and no default targets configured
Note: This will be an error in a future version of Lake.
Hint: This warning (or error) can be suppressed with '--allow-empty'.
Nothing to build.
EOF
) -f $cfg_file build
test_err_diff <(cat << 'EOF'
warning: no targets specified and no default targets configured
Note: This will be an error in a future version of Lake.
Hint: This warning (or error) can be suppressed with '--allow-empty'.
EOF
) -f $cfg_file build --wfail
test_out_diff <(cat << 'EOF'
Nothing to build.
EOF
) -f $cfg_file build --allow-empty --wfail
# Test the warning is not printed on a regular build.
# The configurations use `globs = []` to minimize build variance,
# and to verify that empty globs do not count as no jobs.
test_out_diff <(cat << 'EOF'
Build completed successfully (1 job).
EOF
) -f $cfg_file build Lib
}
# Test Lean configuration with no default targets
./clean.sh
echo "# TEST: lakefile.lean"
test_empty_build lakefile.lean
# Test TOML configuration with no default targets
./clean.sh
echo "# TEST: lakefile.toml"
test_empty_build lakefile.toml
# Cleanup
rm -f produced.*