diff options
-rw-r--r-- | Cargo.lock | 476 | ||||
-rw-r--r-- | crates/driver/src/driver.rs | 409 | ||||
-rw-r--r-- | crates/driver/src/parse.rs | 95 | ||||
-rw-r--r-- | crates/driver/src/recipe.rs | 97 | ||||
-rw-r--r-- | crates/rebel-common/Cargo.toml (renamed from crates/common/Cargo.toml) | 0 | ||||
-rw-r--r-- | crates/rebel-common/src/error.rs (renamed from crates/common/src/error.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-common/src/lib.rs (renamed from crates/common/src/lib.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-common/src/string_hash.rs (renamed from crates/common/src/string_hash.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-common/src/types.rs (renamed from crates/common/src/types.rs) | 25 | ||||
-rw-r--r-- | crates/rebel-parse/Cargo.toml | 20 | ||||
-rw-r--r-- | crates/rebel-parse/benches/recipe.rs | 21 | ||||
-rw-r--r-- | crates/rebel-parse/examples/parse-string.rs | 66 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast.rs | 223 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/mod.rs | 3 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/recipe.rs | 166 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/task_ref.rs | 65 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/tokenize.rs | 104 | ||||
-rw-r--r-- | crates/rebel-parse/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/rebel-parse/src/token.rs | 68 | ||||
-rw-r--r-- | crates/rebel-resolve/Cargo.toml | 15 | ||||
-rw-r--r-- | crates/rebel-resolve/src/args.rs (renamed from crates/driver/src/args.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-resolve/src/context.rs (renamed from crates/driver/src/context.rs) | 124 | ||||
-rw-r--r-- | crates/rebel-resolve/src/lib.rs (renamed from crates/driver/src/resolve.rs) | 79 | ||||
-rw-r--r-- | crates/rebel-resolve/src/paths.rs (renamed from crates/driver/src/paths.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-resolve/src/pin.rs (renamed from crates/driver/src/pin.rs) | 12 | ||||
-rw-r--r-- | crates/rebel-resolve/src/task.rs (renamed from crates/driver/src/task.rs) | 41 | ||||
-rw-r--r-- | crates/rebel-runner/Cargo.toml (renamed from crates/runner/Cargo.toml) | 4 | ||||
-rw-r--r-- | crates/rebel-runner/src/init.rs (renamed from crates/runner/src/init.rs) | 2 | ||||
-rw-r--r-- | crates/rebel-runner/src/jobserver.rs (renamed from crates/runner/src/jobserver.rs) | 17 | ||||
-rw-r--r-- | crates/rebel-runner/src/lib.rs (renamed from crates/runner/src/lib.rs) | 18 | ||||
-rw-r--r-- | crates/rebel-runner/src/ns.rs (renamed from crates/runner/src/ns.rs) | 2 | ||||
-rw-r--r-- | crates/rebel-runner/src/paths.rs (renamed from crates/runner/src/paths.rs) | 2 | ||||
-rw-r--r-- | crates/rebel-runner/src/tar.rs (renamed from crates/runner/src/tar.rs) | 2 | ||||
-rw-r--r-- | crates/rebel-runner/src/task.rs (renamed from crates/runner/src/task.rs) | 42 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/checkable.rs (renamed from crates/runner/src/util/checkable.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/cjson.rs (renamed from crates/runner/src/util/cjson.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/clone.rs (renamed from crates/runner/src/util/clone.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/fs.rs (renamed from crates/runner/src/util/fs.rs) | 4 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/mod.rs (renamed from crates/runner/src/util/mod.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/stack.rs (renamed from crates/runner/src/util/stack.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/steal.rs (renamed from crates/runner/src/util/steal.rs) | 0 | ||||
-rw-r--r-- | crates/rebel-runner/src/util/unix.rs (renamed from crates/runner/src/util/unix.rs) | 13 | ||||
-rw-r--r-- | crates/rebel/Cargo.toml (renamed from crates/driver/Cargo.toml) | 16 | ||||
-rw-r--r-- | crates/rebel/src/driver.rs | 481 | ||||
-rw-r--r-- | crates/rebel/src/main.rs (renamed from crates/driver/src/main.rs) | 46 | ||||
-rw-r--r-- | crates/rebel/src/recipe.rs | 167 | ||||
-rw-r--r-- | crates/rebel/src/template.rs (renamed from crates/driver/src/template.rs) | 29 | ||||
-rw-r--r-- | examples/pins.yml.example | 6 | ||||
-rw-r--r-- | examples/recipes/bar/build.yml (renamed from examples/recipes/bar.yml) | 0 | ||||
-rw-r--r-- | examples/recipes/binutils/build.yml (renamed from examples/recipes/binutils@2.41.yml) | 22 | ||||
-rw-r--r-- | examples/recipes/busybox/build.yml (renamed from examples/recipes/busybox@1.36.1.yml) | 28 | ||||
-rw-r--r-- | examples/recipes/e2fsprogs/build.yml (renamed from examples/recipes/e2fsprogs@1.47.0.yml) | 22 | ||||
-rw-r--r-- | examples/recipes/foo/build.yml (renamed from examples/recipes/foo.yml) | 0 | ||||
-rw-r--r-- | examples/recipes/gcc/build.libgcc-initial.yml (renamed from examples/recipes/libgcc-initial@13.2.0.yml) | 16 | ||||
-rw-r--r-- | examples/recipes/gcc/build.libgcc.yml (renamed from examples/recipes/libgcc@13.2.0.yml) | 10 | ||||
-rw-r--r-- | examples/recipes/gcc/build.libs.yml (renamed from examples/recipes/gcc-libs@13.2.0.yml) | 12 | ||||
-rw-r--r-- | examples/recipes/gcc/build.yml (renamed from examples/recipes/gcc@13.2.0.yml) | 26 | ||||
-rw-r--r-- | examples/recipes/glibc/build.yml (renamed from examples/recipes/glibc@2.38.yml) | 30 | ||||
-rw-r--r-- | examples/recipes/gmp/build.recipe | 69 | ||||
-rw-r--r-- | examples/recipes/gmp/build.yml (renamed from examples/recipes/gmp@6.3.0.yml) | 22 | ||||
-rw-r--r-- | examples/recipes/image/build.yml (renamed from examples/recipes/image.yml) | 22 | ||||
-rw-r--r-- | examples/recipes/linux/build.uapi-headers.yml (renamed from examples/recipes/linux-uapi-headers@6.5.5.yml) | 10 | ||||
-rw-r--r-- | examples/recipes/linux/build.yml (renamed from examples/recipes/linux@6.5.5.yml) | 22 | ||||
-rw-r--r-- | examples/recipes/ls/build.yml (renamed from examples/recipes/ls.yml) | 0 | ||||
-rw-r--r-- | examples/recipes/make_ext4fs/build.yml (renamed from examples/recipes/make_ext4fs@2020-01-05.yml) | 12 | ||||
-rw-r--r-- | examples/recipes/mpc/build.yml (renamed from examples/recipes/mpc@1.3.1.yml) | 22 | ||||
-rw-r--r-- | examples/recipes/mpfr/build.yml (renamed from examples/recipes/mpfr@4.2.1.yml) | 22 | ||||
-rw-r--r-- | examples/recipes/toolchain/build.yml (renamed from examples/recipes/toolchain.yml) | 4 | ||||
-rw-r--r-- | examples/recipes/zlib/build.yml (renamed from examples/recipes/zlib@1.3.yml) | 22 |
69 files changed, 2255 insertions, 1106 deletions
@@ -4,9 +4,9 @@ version = 3 [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -18,36 +18,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -79,15 +79,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec", @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "capctl" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bee5dac8774a3495f734563416fe6de9ac762ccf762ec7653df52914435da86" +checksum = "4a6e71767585f51c2a33fed6d67147ec0343725fc3c03bf4b89fe67fede56aa5" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -119,12 +119,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" @@ -133,10 +130,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] name = "clap" -version = "4.4.6" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -144,33 +147,34 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.60", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -179,6 +183,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] +name = "condtype" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" + +[[package]] name = "constant_time_eq" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -186,9 +196,9 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -221,6 +231,31 @@ dependencies = [ ] [[package]] +name = "divan" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d567df2c9c2870a43f3f2bd65aaeb18dbce1c18f217c3e564b4fbaeb3ee56c" +dependencies = [ + "cfg-if", + "clap", + "condtype", + "divan-macros", + "libc", + "regex-lite", +] + +[[package]] +name = "divan-macros" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27540baf49be0d484d8f0130d7d8da3011c32a44d4fc873368154f1510e574a2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] name = "enum-kinds" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -238,15 +273,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -261,9 +306,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.4.0" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39b3bc2a8f715298032cf5087e58573809374b08160aa7d750582bdb82d2683" +checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" dependencies = [ "log", "pest", @@ -275,15 +320,15 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hex" @@ -296,9 +341,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -306,15 +351,15 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "lazy_static" @@ -324,50 +369,41 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] -name = "log" -version = "0.4.20" +name = "linux-raw-sys" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] -name = "memchr" -version = "2.6.3" +name = "log" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "memchr" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "cfg-if", + "cfg_aliases", "libc", ] [[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] name = "olpc-cjson" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -380,15 +416,39 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "peg" +version = "0.8.2" +source = "git+https://github.com/kevinmehall/rust-peg.git#2fc1cadaa1efcf47c867715d063ac2d7296945c6" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.2" +source = "git+https://github.com/kevinmehall/rust-peg.git#2fc1cadaa1efcf47c867715d063ac2d7296945c6" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.2" +source = "git+https://github.com/kevinmehall/rust-peg.git#2fc1cadaa1efcf47c867715d063ac2d7296945c6" [[package]] name = "pest" -version = "2.7.4" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -397,9 +457,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.4" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" dependencies = [ "pest", "pest_generator", @@ -407,22 +467,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.4" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.60", ] [[package]] name = "pest_meta" -version = "2.7.4" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", @@ -431,18 +491,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -452,16 +512,14 @@ name = "rebel" version = "0.1.0" dependencies = [ "clap", - "deb-version", - "enum-kinds", "handlebars", "indoc", "lazy_static", "nix", - "nom", "rebel-common", + "rebel-parse", + "rebel-resolve", "rebel-runner", - "scoped-tls-hkt", "serde", "serde_yaml", "walkdir", @@ -476,6 +534,26 @@ dependencies = [ ] [[package]] +name = "rebel-parse" +version = "0.1.0" +dependencies = [ + "clap", + "divan", + "peg", + "rebel-common", +] + +[[package]] +name = "rebel-resolve" +version = "0.1.0" +dependencies = [ + "deb-version", + "enum-kinds", + "rebel-common", + "serde", +] + +[[package]] name = "rebel-runner" version = "0.1.0" dependencies = [ @@ -497,18 +575,37 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] +name = "regex-lite" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] name = "ryu" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -520,36 +617,30 @@ dependencies = [ ] [[package]] -name = "scoped-tls-hkt" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ddc765d3410d9f6c6ca071bf0b67f6b01e3ec4595dc3892f02677e75819dddc" - -[[package]] name = "serde" -version = "1.0.188" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -558,9 +649,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.25" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", @@ -582,9 +673,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -605,9 +696,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -632,23 +723,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57d3e5629bb07f004134a87a2d3cb6272f0a455942401f35776e3f17ecc8d955" [[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.60", ] [[package]] @@ -680,9 +781,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uds" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3803a8c885a33e84f898c82c3e72dbd5b2e807da66443731065a509fb11c6c9f" +checksum = "885c31f06fce836457fe3ef09a59f83fe8db95d270b11cd78f40a4666c4d1661" dependencies = [ "libc", ] @@ -695,18 +796,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "utf8parse" @@ -722,52 +823,39 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] -name = "winapi" -version = "0.3.9" +name = "winapi-util" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-sys 0.52.0", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "winapi", + "windows-targets 0.48.5", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", ] [[package]] @@ -776,13 +864,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -792,46 +896,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] name = "xattr" -version = "1.0.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", + "linux-raw-sys", + "rustix", ] diff --git a/crates/driver/src/driver.rs b/crates/driver/src/driver.rs deleted file mode 100644 index d0abbcb..0000000 --- a/crates/driver/src/driver.rs +++ /dev/null @@ -1,409 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - os::unix::{net::UnixStream, prelude::*}, -}; - -use indoc::indoc; -use nix::poll; - -use common::{error::*, string_hash::*, types::*}; -use runner::Runner; - -use crate::{ - context::{Context, TaskRef}, - paths, resolve, - task::*, - template, -}; - -#[derive(Debug)] -pub struct CompletionState<'ctx> { - ctx: &'ctx Context, - tasks_done: HashMap<TaskRef<'ctx>, TaskOutput>, -} - -impl<'ctx> CompletionState<'ctx> { - pub fn new(ctx: &'ctx Context) -> Self { - CompletionState { - ctx, - tasks_done: Default::default(), - } - } - - // Treats both "depends" and "inherit" as dependencies - fn deps_satisfied(&self, task_ref: &TaskRef) -> bool { - resolve::get_dependent_tasks(self.ctx, task_ref) - .map_err(|_| Error::new(format!("invalid dependency for {}", task_ref))) - .unwrap() - .into_iter() - .all(|dep| self.tasks_done.contains_key(&dep)) - } - - fn fetch_deps(&self, task: &TaskRef<'ctx>) -> Result<Vec<Dependency>> { - let task_def = &self.ctx[task]; - task_def - .fetch - .iter() - .map(|Fetch { name, sha256 }| { - Ok(Dependency::Fetch { - name: template::ENGINE - .eval_raw(name, &task.args) - .with_context(|| { - format!("Failed to evaluate fetch filename for task {}", task) - })?, - target_dir: paths::TASK_DLDIR.to_string(), - sha256: *sha256, - }) - }) - .collect() - } - - fn task_deps(&self, task: &TaskRef<'ctx>) -> Result<HashSet<Dependency>> { - Ok(self - .fetch_deps(task)? - .into_iter() - .chain( - resolve::runtime_depends( - self.ctx, - self.ctx - .get_build_depends(task) - .with_context(|| format!("invalid build depends for {}", task))?, - ) - .expect("invalid runtime depends of build_depends") - .into_iter() - .filter_map(|dep| self.tasks_done[&dep.task].outputs.get(dep.output)) - .map(|&output| Dependency::Task { - output, - path: "".to_string(), - }), - ) - .chain( - resolve::runtime_depends( - self.ctx, - self.ctx - .get_host_depends(task) - .with_context(|| format!("invalid depends for {}", task))?, - ) - .expect("invalid runtime depends of host_depends") - .into_iter() - .filter_map(|dep| self.tasks_done[&dep.task].outputs.get(dep.output)) - .map(|&output| Dependency::Task { - output, - path: paths::TASK_SYSROOT.to_string(), - }), - ) - .collect()) - } - - fn task_inherit_chain(&self, task_ref: &TaskRef<'ctx>) -> Vec<LayerHash> { - let inherit = match self - .ctx - .get_inherit_depend(task_ref) - .expect("invalid inherit depends") - { - Some(inherit) => inherit, - None => return vec![], - }; - - let mut chain = self.task_inherit_chain(&inherit); - if let Some(layer) = self.tasks_done[&inherit].layer { - chain.push(layer); - } - chain - } - - fn print_summary(&self) { - println!(); - println!("Summary:"); - - let mut tasks: Box<[_]> = self.tasks_done.iter().collect(); - tasks.sort_by_cached_key(|(task, _)| format!("{:#}", task)); - for (task_ref, task) in tasks.iter() { - println!(); - println!("{:#}", task_ref); - if let Some(hash) = task.input_hash { - println!(" input: {}", hash); - } - if let Some(hash) = task.layer { - println!(" layer: {}", hash); - } - if !task.outputs.is_empty() { - println!(" outputs:"); - - let mut outputs: Box<[_]> = task.outputs.iter().collect(); - outputs.sort_by_key(|(output, _)| *output); - for (output, hash) in outputs.iter() { - println!(" {}: {}", output, hash); - } - } - } - } -} - -enum SpawnResult { - Spawned(UnixStream), - Skipped(TaskOutput), -} - -#[derive(Debug)] -pub struct Driver<'ctx> { - rdeps: HashMap<TaskRef<'ctx>, Vec<TaskRef<'ctx>>>, - force_run: HashSet<TaskRef<'ctx>>, - tasks_blocked: HashSet<TaskRef<'ctx>>, - tasks_runnable: Vec<TaskRef<'ctx>>, - tasks_running: HashMap<RawFd, (UnixStream, TaskRef<'ctx>)>, - state: CompletionState<'ctx>, -} - -impl<'ctx> Driver<'ctx> { - pub fn new( - ctx: &'ctx Context, - taskset: HashSet<TaskRef<'ctx>>, - force_run: HashSet<TaskRef<'ctx>>, - ) -> Result<Self> { - let mut driver = Driver { - rdeps: Default::default(), - force_run, - tasks_blocked: Default::default(), - tasks_runnable: Default::default(), - tasks_running: Default::default(), - state: CompletionState::new(ctx), - }; - - for task in taskset { - let mut has_depends = false; - for dep in resolve::get_dependent_tasks(ctx, &task) - .map_err(|_| Error::new(format!("invalid dependency for {}", task)))? - { - let rdep = driver.rdeps.entry(dep.clone()).or_default(); - rdep.push(task.clone()); - has_depends = true; - } - - if has_depends { - driver.tasks_blocked.insert(task); - } else { - driver.tasks_runnable.push(task); - } - } - - Ok(driver) - } - - fn task_setup(task_ref: &TaskRef<'ctx>) -> Vec<&'static str> { - let mut ret = vec![indoc! {" - export PATH={{build.prefix}}/sbin:{{build.prefix}}/bin:$PATH - cd {{workdir}} - - export SOURCE_DATE_EPOCH=1 - - export AR_FOR_BUILD=ar - export AS_FOR_BUILD=as - export DLLTOOL_FOR_BUILD=dlltool - export CC_FOR_BUILD=gcc - export CXX_FOR_BUILD=g++ - export GCC_FOR_BUILD=gcc - export GFORTRAN_FOR_BUILD=gfortran - export GOC_FOR_BUILD=goc - export LD_FOR_BUILD=ld - export LIPO_FOR_BUILD=lipo - export NM_FOR_BUILD=nm - export OBJCOPY_FOR_BUILD=objcopy - export OBJDUMP_FOR_BUILD=objdump - export RANLIB_FOR_BUILD=ranlib - export STRIP_FOR_BUILD=strip - export WINDRES_FOR_BUILD=windres - export WINDMC_FOR_BUILD=windmc - "}]; - - if task_ref.args.contains_key("build_to_host") { - ret.push(indoc! {" - export AR={{build_to_host.cross_compile}}ar - export AS={{build_to_host.cross_compile}}as - export DLLTOOL={{build_to_host.cross_compile}}dlltool - export CC={{build_to_host.cross_compile}}gcc - export CXX={{build_to_host.cross_compile}}g++ - export GCC={{build_to_host.cross_compile}}gcc - export GFORTRAN={{build_to_host.cross_compile}}gfortran - export GOC={{build_to_host.cross_compile}}goc - export LD={{build_to_host.cross_compile}}ld - export LIPO={{build_to_host.cross_compile}}lipo - export NM={{build_to_host.cross_compile}}nm - export OBJCOPY={{build_to_host.cross_compile}}objcopy - export OBJDUMP={{build_to_host.cross_compile}}objdump - export RANLIB={{build_to_host.cross_compile}}ranlib - export STRIP={{build_to_host.cross_compile}}strip - export WINDRES={{build_to_host.cross_compile}}windres - export WINDMC={{build_to_host.cross_compile}}windmc - "}); - } - - if task_ref.args.contains_key("build_to_target") { - ret.push(indoc! {" - export AR_FOR_TARGET={{build_to_target.cross_compile}}ar - export AS_FOR_TARGET={{build_to_target.cross_compile}}as - export DLLTOOL_FOR_TARGET={{build_to_target.cross_compile}}dlltool - export CC_FOR_TARGET={{build_to_target.cross_compile}}gcc - export CXX_FOR_TARGET={{build_to_target.cross_compile}}g++ - export GCC_FOR_TARGET={{build_to_target.cross_compile}}gcc - export GFORTRAN_FOR_TARGET={{build_to_target.cross_compile}}gfortran - export GOC_FOR_TARGET={{build_to_target.cross_compile}}goc - export LD_FOR_TARGET={{build_to_target.cross_compile}}ld - export LIPO_FOR_TARGET={{build_to_target.cross_compile}}lipo - export NM_FOR_TARGET={{build_to_target.cross_compile}}nm - export OBJCOPY_FOR_TARGET={{build_to_target.cross_compile}}objcopy - export OBJDUMP_FOR_TARGET={{build_to_target.cross_compile}}objdump - export RANLIB_FOR_TARGET={{build_to_target.cross_compile}}ranlib - export STRIP_FOR_TARGET={{build_to_target.cross_compile}}strip - export WINDRES_FOR_TARGET={{build_to_target.cross_compile}}windres - export WINDMC_FOR_TARGET={{build_to_target.cross_compile}}windmc - "}); - } - ret - } - - fn update_runnable(&mut self, task_ref: TaskRef<'ctx>, task_output: TaskOutput) { - let rdeps = self.rdeps.get(&task_ref); - - self.state.tasks_done.insert(task_ref, task_output); - - for rdep in rdeps.unwrap_or(&Vec::new()) { - if !self.tasks_blocked.contains(rdep) { - continue; - } - if self.state.deps_satisfied(rdep) { - self.tasks_blocked.remove(rdep); - self.tasks_runnable.push(rdep.clone()); - } - } - } - - fn spawn_task(&self, task_ref: &TaskRef<'ctx>, runner: &Runner) -> Result<SpawnResult> { - let task_def = &self.state.ctx[task_ref]; - if task_def.action.is_empty() { - println!("Skipping empty task {:#}", task_ref); - return Ok(SpawnResult::Skipped(TaskOutput::default())); - } - - let task_deps = self.state.task_deps(task_ref)?; - let task_output = task_def - .output - .iter() - .map(|(name, Output { path, .. })| { - let output_path = if let Some(path) = path { - format!("{}/{}", paths::TASK_DESTDIR, path) - } else { - paths::TASK_DESTDIR.to_string() - }; - (name.clone(), output_path) - }) - .collect(); - - let inherit_chain = self.state.task_inherit_chain(task_ref); - - let mut run = Self::task_setup(task_ref); - run.push(&task_def.action.run); - - let command = template::ENGINE - .eval(&run.concat(), &task_ref.args) - .with_context(|| { - format!("Failed to evaluate command template for task {}", task_ref) - })?; - - let rootfs = self.state.ctx.get_rootfs(); - let task = Task { - label: format!("{:#}", task_ref), - command, - workdir: paths::TASK_WORKDIR.to_string(), - rootfs: rootfs.0, - inherit: inherit_chain, - depends: task_deps, - outputs: task_output, - pins: HashMap::from([rootfs.clone()]), - force_run: self.force_run.contains(task_ref), - }; - - Ok(SpawnResult::Spawned(runner.spawn(&task))) - } - - fn run_task(&mut self, task_ref: TaskRef<'ctx>, runner: &Runner) -> Result<()> { - match self.spawn_task(&task_ref, runner)? { - SpawnResult::Spawned(socket) => { - assert!(self - .tasks_running - .insert(socket.as_raw_fd(), (socket, task_ref)) - .is_none()); - } - SpawnResult::Skipped(result) => { - self.update_runnable(task_ref, result); - } - } - Ok(()) - } - - fn run_tasks(&mut self, runner: &Runner) -> Result<()> { - while let Some(task_ref) = self.tasks_runnable.pop() { - self.run_task(task_ref, runner)?; - } - Ok(()) - } - - fn wait_for_task(&mut self) -> Result<()> { - let mut pollfds: Vec<_> = self - .tasks_running - .values() - .map(|(socket, _)| poll::PollFd::new(socket, poll::PollFlags::POLLIN)) - .collect(); - - while poll::poll(&mut pollfds, -1).context("poll()")? == 0 {} - - let pollevents: Vec<_> = pollfds - .into_iter() - .map(|pollfd| { - ( - pollfd.as_fd().as_raw_fd(), - pollfd.revents().expect("Unknown events in poll() return"), - ) - }) - .collect(); - - for (fd, events) in pollevents { - if !events.contains(poll::PollFlags::POLLIN) { - if events.intersects(!poll::PollFlags::POLLIN) { - return Err(Error::new( - "Unexpected error status for socket file descriptor", - )); - } - continue; - } - - let (socket, task_ref) = self.tasks_running.remove(&fd).unwrap(); - - let task_output = Runner::result(&socket)?; - self.update_runnable(task_ref, task_output); - } - - Ok(()) - } - - fn is_empty(&self) -> bool { - self.tasks_runnable.is_empty() && self.tasks_running.is_empty() - } - - fn is_done(&self) -> bool { - self.is_empty() && self.tasks_blocked.is_empty() - } - - pub fn run(&mut self, runner: &Runner) -> Result<()> { - while !self.is_empty() { - self.run_tasks(runner)?; - self.wait_for_task()?; - } - - assert!(self.is_done(), "No runnable tasks left"); - - self.state.print_summary(); - - Ok(()) - } -} diff --git a/crates/driver/src/parse.rs b/crates/driver/src/parse.rs deleted file mode 100644 index aad9360..0000000 --- a/crates/driver/src/parse.rs +++ /dev/null @@ -1,95 +0,0 @@ -use nom::{ - bytes::complete::{tag, take_while1}, - combinator::{all_consuming, opt}, - error::ParseError, - Err, IResult, InputLength, Parser, -}; - -#[derive(Debug, Clone, Copy)] -pub struct Task<'a> { - pub recipe: &'a str, - pub task: &'a str, - pub host: Option<&'a str>, - pub target: Option<&'a str>, -} - -#[derive(Debug, Clone, Copy)] -pub struct TaskFlags { - pub force_run: bool, -} - -fn is_name_char(c: char) -> bool { - matches!(c, 'a'..='z' | 'A' ..='Z' | '0'..='9' | '_' | '-') -} - -fn name(input: &str) -> IResult<&str, &str> { - take_while1(is_name_char)(input) -} - -fn task_id(input: &str) -> IResult<&str, (&str, &str)> { - let (input, recipe) = name(input)?; - let (input, _) = tag(":")(input)?; - let (input, task) = name(input)?; - Ok((input, (recipe, task))) -} - -fn task_arg_target(input: &str) -> IResult<&str, &str> { - let (input, _) = tag(":")(input)?; - let (input, target) = name(input)?; - Ok((input, target)) -} - -fn task_args(input: &str) -> IResult<&str, (Option<&str>, Option<&str>)> { - let (input, _) = tag("/")(input)?; - let (input, host) = opt(name)(input)?; - let (input, target) = opt(task_arg_target)(input)?; - - Ok((input, (host, target))) -} - -fn task(input: &str) -> IResult<&str, Task> { - let (input, (recipe, task)) = task_id(input)?; - let (input, args) = opt(task_args)(input)?; - - let (host, target) = args.unwrap_or_default(); - - Ok(( - input, - Task { - recipe, - task, - host, - target, - }, - )) -} - -fn task_flags(input: &str) -> IResult<&str, TaskFlags> { - let (input, force_run) = opt(tag("+"))(input)?; - - Ok(( - input, - TaskFlags { - force_run: force_run.is_some(), - }, - )) -} - -fn task_with_flags(input: &str) -> IResult<&str, (Task, TaskFlags)> { - let (input, task) = task(input)?; - let (input, flags) = task_flags(input)?; - - Ok((input, (task, flags))) -} - -fn parse_all<I, O, E: ParseError<I>, F>(f: F, input: I) -> Result<O, Err<E>> -where - I: InputLength, - F: Parser<I, O, E>, -{ - all_consuming(f)(input).map(|(_, result)| result) -} - -pub fn parse_task_with_flags(input: &str) -> Option<(Task, TaskFlags)> { - parse_all(task_with_flags, input).ok() -} diff --git a/crates/driver/src/recipe.rs b/crates/driver/src/recipe.rs deleted file mode 100644 index 474096b..0000000 --- a/crates/driver/src/recipe.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::{collections::HashMap, fs::File, path::Path, result}; - -use scoped_tls_hkt::scoped_thread_local; -use serde::{Deserialize, Deserializer}; -use walkdir::WalkDir; - -use common::{error::*, types::*}; - -use crate::task::{RecipeMeta, TaskDef}; - -scoped_thread_local!(static CURRENT_RECIPE: str); - -fn current_recipe() -> String { - CURRENT_RECIPE.with(|current| current.to_string()) -} - -pub fn deserialize_task_id<'de, D>(deserializer: D) -> result::Result<TaskID, D::Error> -where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - struct RecipeTaskID { - recipe: Option<String>, - task: String, - } - let RecipeTaskID { recipe, task } = RecipeTaskID::deserialize(deserializer)?; - Ok(TaskID { - recipe: recipe.unwrap_or_else(current_recipe), - task, - }) -} - -#[derive(Debug, Deserialize)] -struct Recipe { - #[serde(default)] - pub meta: RecipeMeta, - pub tasks: HashMap<String, TaskDef>, -} - -fn read_recipe(path: &Path) -> Result<Recipe> { - let f = File::open(path).context("IO error")?; - - let recipe: Recipe = serde_yaml::from_reader(f) - .map_err(Error::new) - .context("YAML error")?; - - Ok(recipe) -} - -fn is_yml(path: &Path) -> bool { - path.extension() == Some("yml".as_ref()) -} - -pub fn read_recipes<P: AsRef<Path>>(path: P) -> Result<HashMap<TaskID, Vec<TaskDef>>> { - let mut tasks = HashMap::<TaskID, Vec<TaskDef>>::new(); - - for entry in WalkDir::new(path) - .sort_by_file_name() - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry.path(); - if !path.is_file() || !is_yml(path) { - continue; - } - - let stem: &str = match path.file_stem().map(|n| n.to_str()) { - Some(Some(v)) => v, - _ => continue, - }; - let (basename, version) = match stem.split_once('@') { - Some((basename, version)) => (basename, Some(version)), - None => (stem, None), - }; - - let recipe = CURRENT_RECIPE.set(basename, || read_recipe(path))?; - - let mut meta = recipe.meta; - if meta.name.is_empty() { - meta.name = basename.to_string(); - } - if meta.version.is_none() { - meta.version = version.map(|v| v.to_string()); - } - - for (label, mut task) in recipe.tasks { - let task_id = TaskID { - recipe: basename.to_string(), - task: label, - }; - task.meta = meta.clone(); - tasks.entry(task_id).or_default().push(task); - } - } - - Ok(tasks) -} diff --git a/crates/common/Cargo.toml b/crates/rebel-common/Cargo.toml index 954ebe5..954ebe5 100644 --- a/crates/common/Cargo.toml +++ b/crates/rebel-common/Cargo.toml diff --git a/crates/common/src/error.rs b/crates/rebel-common/src/error.rs index ba25af4..ba25af4 100644 --- a/crates/common/src/error.rs +++ b/crates/rebel-common/src/error.rs diff --git a/crates/common/src/lib.rs b/crates/rebel-common/src/lib.rs index 8d630dd..8d630dd 100644 --- a/crates/common/src/lib.rs +++ b/crates/rebel-common/src/lib.rs diff --git a/crates/common/src/string_hash.rs b/crates/rebel-common/src/string_hash.rs index a2b00db..a2b00db 100644 --- a/crates/common/src/string_hash.rs +++ b/crates/rebel-common/src/string_hash.rs diff --git a/crates/common/src/types.rs b/crates/rebel-common/src/types.rs index 32b9182..d3beb70 100644 --- a/crates/common/src/types.rs +++ b/crates/rebel-common/src/types.rs @@ -13,9 +13,30 @@ pub struct TaskID { pub task: String, } +impl TaskID { + pub fn as_ref(&self) -> TaskIDRef<'_> { + TaskIDRef { + recipe: &self.recipe, + task: &self.task, + } + } +} + impl Display for TaskID { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", self.recipe, self.task) + self.as_ref().fmt(f) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct TaskIDRef<'a> { + pub recipe: &'a str, + pub task: &'a str, +} + +impl<'a> Display for TaskIDRef<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}::{}", self.recipe, self.task) } } @@ -39,7 +60,7 @@ pub struct Task { pub command: String, pub workdir: String, pub rootfs: ArchiveHash, - pub inherit: Vec<LayerHash>, + pub ancestors: Vec<LayerHash>, pub depends: HashSet<Dependency>, pub outputs: HashMap<String, String>, pub pins: HashMap<ArchiveHash, String>, diff --git a/crates/rebel-parse/Cargo.toml b/crates/rebel-parse/Cargo.toml new file mode 100644 index 0000000..0233af8 --- /dev/null +++ b/crates/rebel-parse/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rebel-parse" +version = "0.1.0" +authors = ["Matthias Schiffer <mschiffer@universe-factory.net>"] +license = "MIT" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +peg = { git = "https://github.com/kevinmehall/rust-peg.git", version = "0.8.2" } +rebel-common = { path = "../rebel-common" } + +[dev-dependencies] +clap = { version = "4.0.0", features = ["derive"] } +divan = "0.1.14" + +[[bench]] +name = "recipe" +harness = false diff --git a/crates/rebel-parse/benches/recipe.rs b/crates/rebel-parse/benches/recipe.rs new file mode 100644 index 0000000..4cff857 --- /dev/null +++ b/crates/rebel-parse/benches/recipe.rs @@ -0,0 +1,21 @@ +use rebel_parse::{ast, token::TokenStream}; + +fn main() { + divan::main(); +} + +const RECIPE: &str = include_str!("../../../examples/recipes/gmp/build.recipe"); + +#[divan::bench] +fn tokenize() -> TokenStream<'static> { + rebel_parse::tokenize::token_stream(divan::black_box(RECIPE)).unwrap() +} + +#[divan::bench] +fn parse(bencher: divan::Bencher) { + let tokens = tokenize(); + + bencher.bench(|| -> ast::Recipe<'static> { + rebel_parse::recipe::recipe(divan::black_box(&tokens)).unwrap() + }); +} diff --git a/crates/rebel-parse/examples/parse-string.rs b/crates/rebel-parse/examples/parse-string.rs new file mode 100644 index 0000000..190d0fa --- /dev/null +++ b/crates/rebel-parse/examples/parse-string.rs @@ -0,0 +1,66 @@ +use std::{fmt::Debug, process, time::Instant}; + +use clap::{Parser, ValueEnum}; + +use rebel_parse::{recipe, tokenize}; + +#[derive(Clone, Debug, PartialEq, Eq, ValueEnum)] +enum Rule { + Tokenize, + Recipe, + RecipeStmt, + Body, + BodyStmt, + Expr, +} + +#[derive(Clone, Debug, Parser)] +struct Opts { + rule: Rule, + input: String, +} + +fn main() { + let opts: Opts = Opts::parse(); + let input = opts.input.trim(); + + fn as_debug<'a>(v: impl Debug + 'a) -> Box<dyn Debug + 'a> { + Box::new(v) + } + + let start = Instant::now(); + let result = tokenize::token_stream(input); + let dur = Instant::now().duration_since(start); + println!("Tokenization took {} µs", dur.as_micros()); + + let tokens = match result { + Ok(value) => value, + Err(err) => { + println!("{err}"); + process::exit(1); + } + }; + + let start = Instant::now(); + let result = match opts.rule { + Rule::Tokenize => Ok(as_debug(tokens)), + Rule::Recipe => recipe::recipe(&tokens).map(as_debug), + Rule::RecipeStmt => recipe::recipe_stmt(&tokens).map(as_debug), + Rule::Body => recipe::body(&tokens).map(as_debug), + Rule::BodyStmt => recipe::body_stmt(&tokens).map(as_debug), + Rule::Expr => recipe::expr(&tokens).map(as_debug), + }; + if opts.rule != Rule::Tokenize { + let dur = Instant::now().duration_since(start); + println!("Parsing took {} µs", dur.as_micros()); + } + + match result { + Ok(value) => { + println!("{value:#?}"); + } + Err(err) => { + println!("{err}"); + } + }; +} diff --git a/crates/rebel-parse/src/ast.rs b/crates/rebel-parse/src/ast.rs new file mode 100644 index 0000000..648a79e --- /dev/null +++ b/crates/rebel-parse/src/ast.rs @@ -0,0 +1,223 @@ +use crate::token; + +pub type Recipe<'a> = Vec<RecipeStmt<'a>>; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RecipeStmt<'a> { + BodyStmt(BodyStmt<'a>), + Fetch { + name: Ident<'a>, + body: Body<'a>, + }, + Task { + name: Ident<'a>, + params: Vec<FuncParam<'a>>, + body: Body<'a>, + }, +} + +pub type Body<'a> = Vec<BodyStmt<'a>>; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BodyStmt<'a> { + Assign { + left: Box<TypedExpr<'a>>, + op: Option<OpBinary>, + right: Box<Expr<'a>>, + }, +} + +impl<'a> BodyStmt<'a> { + pub(crate) fn assign(left: TypedExpr<'a>, op: Option<OpBinary>, right: Expr<'a>) -> Self { + BodyStmt::Assign { + left: Box::new(left), + op, + right: Box::new(right), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Expr<'a> { + Binary { + left: Box<Expr<'a>>, + op: OpBinary, + right: Box<Expr<'a>>, + }, + Unary { + op: OpUnary, + expr: Box<Expr<'a>>, + }, + Apply { + expr: Box<Expr<'a>>, + params: Vec<Expr<'a>>, + }, + Method { + expr: Box<Expr<'a>>, + method: Ident<'a>, + params: Vec<Expr<'a>>, + }, + Index { + expr: Box<Expr<'a>>, + index: Box<Expr<'a>>, + }, + Field { + expr: Box<Expr<'a>>, + field: Ident<'a>, + }, + Paren(Box<Expr<'a>>), + Path(Path<'a>), + Literal(Literal<'a>), +} + +impl<'a> Expr<'a> { + pub(crate) fn binary(left: Expr<'a>, op: OpBinary, right: Expr<'a>) -> Self { + Expr::Binary { + left: Box::new(left), + op, + right: Box::new(right), + } + } + + pub(crate) fn unary(op: OpUnary, expr: Expr<'a>) -> Self { + Expr::Unary { + op, + expr: Box::new(expr), + } + } + + pub(crate) fn apply(expr: Expr<'a>, params: Vec<Expr<'a>>) -> Self { + Expr::Apply { + expr: Box::new(expr), + params, + } + } + + pub(crate) fn method(expr: Expr<'a>, method: Ident<'a>, params: Vec<Expr<'a>>) -> Self { + Expr::Method { + expr: Box::new(expr), + method, + params, + } + } + + pub(crate) fn index(expr: Expr<'a>, index: Expr<'a>) -> Self { + Expr::Index { + expr: Box::new(expr), + index: Box::new(index), + } + } + + pub(crate) fn field(expr: Expr<'a>, field: Ident<'a>) -> Self { + Expr::Field { + expr: Box::new(expr), + field, + } + } + + pub(crate) fn paren(expr: Expr<'a>) -> Self { + Expr::Paren(Box::new(expr)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypedExpr<'a> { + pub expr: Expr<'a>, + pub typ: Option<Expr<'a>>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FuncParam<'a> { + pub name: Ident<'a>, + pub typ: Expr<'a>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Literal<'a> { + Unit, + Boolean(bool), + Integer(u64), + String(Vec<StringPiece<'a>>), + Tuple(Vec<Expr<'a>>), + Array(Vec<Expr<'a>>), + Map(Vec<MapEntry<'a>>), +} + +impl<'a> Literal<'a> { + pub(crate) fn number(s: &'a str) -> Result<Self, &'static str> { + let (radix, rest) = if let Some(rest) = s.strip_prefix("0x") { + (16, rest) + } else if let Some(rest) = s.strip_prefix("0o") { + (8, rest) + } else if let Some(rest) = s.strip_prefix("0b") { + (2, rest) + } else { + (10, s) + }; + let digits = rest.replace('_', ""); + let value = u64::from_str_radix(&digits, radix).or(Err("number"))?; + Ok(Literal::Integer(value)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum StringPiece<'a> { + Chars(&'a str), + Escape(char), + Interp(Expr<'a>), +} + +impl<'a> TryFrom<&token::StringPiece<'a>> for StringPiece<'a> { + type Error = &'static str; + + fn try_from(value: &token::StringPiece<'a>) -> Result<Self, Self::Error> { + use crate::recipe; + + Ok(match value { + token::StringPiece::Chars(chars) => StringPiece::Chars(chars), + token::StringPiece::Escape(c) => StringPiece::Escape(*c), + token::StringPiece::Interp(tokens) => StringPiece::Interp( + recipe::expr(tokens).or(Err("Invalid expression in string interpolation"))?, + ), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MapEntry<'a> { + pub key: &'a str, + pub value: Expr<'a>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OpUnary { + Not, + Neg, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OpBinary { + Add, + Sub, + Mul, + Div, + Rem, + And, + Or, + Eq, + Lt, + Le, + Ne, + Ge, + Gt, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Path<'a> { + pub components: Vec<Ident<'a>>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Ident<'a> { + pub name: &'a str, +} diff --git a/crates/rebel-parse/src/grammar/mod.rs b/crates/rebel-parse/src/grammar/mod.rs new file mode 100644 index 0000000..de06991 --- /dev/null +++ b/crates/rebel-parse/src/grammar/mod.rs @@ -0,0 +1,3 @@ +pub mod recipe; +pub mod task_ref; +pub mod tokenize; diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs new file mode 100644 index 0000000..49b1689 --- /dev/null +++ b/crates/rebel-parse/src/grammar/recipe.rs @@ -0,0 +1,166 @@ +use crate::ast::{self, Expr}; +use crate::token::*; + +pub use rules::*; + +peg::parser! { + pub grammar rules<'a>() for TokenStream<'a> { + use ast::OpBinary::*; + use ast::OpUnary::*; + + pub rule recipe() -> ast::Recipe<'a> + = recipe:recipe_stmt()* { recipe } + + pub rule recipe_stmt() -> ast::RecipeStmt<'a> + = keyword_fetch() name:ident() p('{') body:body() p('}') { + ast::RecipeStmt::Fetch { name, body: Vec::new() } + } + / keyword_task() name:ident() p('(') params:func_params() p(')') + p('{') body:body() p('}') { + ast::RecipeStmt::Task { name, params, body } + } + / stmt:body_stmt() { + ast::RecipeStmt::BodyStmt(stmt) + } + + pub rule body() -> ast::Body<'a> + = body:body_stmt()* { body } + + pub rule body_stmt() -> ast::BodyStmt<'a> + = left:typed_expr() op:assign_op() right:expr() p(';') { + ast::BodyStmt::assign(left, op, right) + } + + rule assign_op() -> Option<ast::OpBinary> + = p2('+', '=') { Some(Add) } + / p2('-', '=') { Some(Sub) } + / p2('*', '=') { Some(Mul) } + / p2('/', '=') { Some(Div) } + / p2('%', '=') { Some(Rem) } + / p('=') { None } + + rule typed_expr() -> ast::TypedExpr<'a> + = expr:expr() typ:tagged(<p(':')>, <expr()>)? { ast::TypedExpr { expr, typ } } + + pub rule expr() -> Expr<'a> = precedence! { + left:(@) p2('|', '|') right:@ { Expr::binary(left, Or, right) } + -- + left:(@) p2('&', '&') right:@ { Expr::binary(left, And, right) } + -- + left:(@) p2('=', '=') right:@ { Expr::binary(left, Eq, right) } + left:(@) p2('!', '=') right:@ { Expr::binary(left, Ne, right) } + left:(@) p('<') right:@ { Expr::binary(left, Lt, right) } + left:(@) p('>') right:@ { Expr::binary(left, Gt, right) } + left:(@) p2('<', '=') right:@ { Expr::binary(left, Le, right) } + left:(@) p2('>', '=') right:@ { Expr::binary(left, Ge, right) } + -- + left:(@) p('+') right:@ { Expr::binary(left, Add, right) } + left:(@) p('-') right:@ { Expr::binary(left, Sub, right) } + -- + left:(@) p('*') right:@ { Expr::binary(left, Mul, right) } + left:(@) p('/') right:@ { Expr::binary(left, Div, right) } + left:(@) p('%') right:@ { Expr::binary(left, Rem, right) } + -- + p('-') expr:@ { Expr::unary(Neg, expr) } + p('!') expr:@ { Expr::unary(Not, expr) } + -- + expr:@ p('(') params:call_params() p(')') { + Expr::apply(expr, params) + } + expr:@ p('[') index:expr() p(']') { Expr::index(expr, index) } + -- + expr:@ p('.') method:field() p('(') params:call_params() p(')') { + Expr::method(expr, method, params) + } + expr:@ p('.') field:field() { Expr::field(expr, field) } + -- + p('(') e:expr() p(')') { Expr::paren(e) } + e:atom() { e } + } + + rule atom() -> Expr<'a> + = lit:literal() { Expr::Literal(lit) } + / path:path() { Expr::Path(path) } + + rule call_params() -> Vec<ast::Expr<'a>> + = args:delimited(<expr()>, <p(',')>) { args } + + rule func_params() -> Vec<ast::FuncParam<'a>> + = params:delimited(<func_param()>, <p(',')>) { params } + + rule func_param() -> ast::FuncParam<'a> + = name:ident() p(':') typ:expr() { ast::FuncParam { name, typ } } + + rule literal() -> ast::Literal<'a> + = keyword_true() { ast::Literal::Boolean(true) } + / keyword_false() { ast::Literal::Boolean(false) } + / [Token::Number(content)] { ? + ast::Literal::number(content) + } + / [Token::String(String { pieces, .. })] { ? + let ast_pieces = pieces.iter().map(|piece| piece.try_into()).collect::<Result<_, _>>()?; + Ok(ast::Literal::String(ast_pieces)) + } + / p('(') p(')') { ast::Literal::Unit } + / p('(') elements:(expr() ** p(',')) p(',')? p(')') { + ast::Literal::Tuple(elements) + } + / p('[') elements:delimited(<expr()>, <p(',')>) p(']') { + ast::Literal::Array(elements) + } + / p('{') entries:delimited(<map_entry()>, <p(',')>) p('}') { + ast::Literal::Map(entries) + } + + rule map_entry() -> ast::MapEntry<'a> + = key:field() p('=') value:expr() { + ast::MapEntry { key: key.name, value } + } + + rule path() -> ast::Path<'a> + = components:ident() ++ p2(':', ':') { ast::Path { components } } + + rule field() -> ast::Ident<'a> + = ident() + / [Token::Number(content)] { + ast::Ident { name: content } + } + + rule p_(ch: char) + = [Token::Punct(Punct(c, Spacing::Joint)) if *c == ch] {} + + rule p(ch: char) -> () + = [Token::Punct(Punct(c, _)) if *c == ch] {} + + rule p2(ch1: char, ch2: char) + = p_(ch1) p(ch2) + + rule ident() -> ast::Ident<'a> + = !keyword() [Token::Ident(name)] { ast::Ident { name } } + + rule keyword() + = keyword_true() + / keyword_false() + / keyword_fetch() + / keyword_task() + + rule keyword_true() + = const_ident("true") + rule keyword_false() + = const_ident("false") + rule keyword_fetch() + = const_ident("fetch") + rule keyword_task() + = const_ident("task") + + rule const_ident(keyword: &str) + = [Token::Ident(name) if *name == keyword] + + rule delimited<T>(expr: rule<T>, delim: rule<()>) -> Vec<T> + = values:(expr() ++ delim()) delim()? { values } + / { Vec::new() } + + rule tagged<T>(tag: rule<()>, value: rule<T>) -> T + = tag() v:value() { v } + } +} diff --git a/crates/rebel-parse/src/grammar/task_ref.rs b/crates/rebel-parse/src/grammar/task_ref.rs new file mode 100644 index 0000000..77d6c5f --- /dev/null +++ b/crates/rebel-parse/src/grammar/task_ref.rs @@ -0,0 +1,65 @@ +pub use rules::*; + +use rebel_common::types::TaskIDRef; + +#[derive(Debug, Clone, Copy)] +pub struct TaskRef<'a> { + pub id: TaskIDRef<'a>, + pub args: TaskArgs<'a>, +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct TaskArgs<'a> { + pub host: Option<&'a str>, + pub target: Option<&'a str>, +} + +#[derive(Debug, Clone, Copy)] +pub struct TaskFlags { + pub force_run: bool, +} + +peg::parser! { + pub grammar rules() for str { + pub rule task_ref_with_flags() -> (TaskRef<'input>, TaskFlags) + = task:task_ref() flags:task_flags() { (task, flags) } + + pub rule task_ref() -> TaskRef<'input> + = id:task_id() args:task_args() { + TaskRef { + id, + args, + } + } + + rule recipe_id() -> &'input str + = $(name() ("/" name())?) + + rule task_id() -> TaskIDRef<'input> + = recipe:recipe_id() "::" task:name() { + TaskIDRef { recipe, task } + } + + rule task_args() -> TaskArgs<'input> + = "@" host:name()? target:tagged(<":">, <name()>)? { + TaskArgs { + host, + target, + } + } + / { Default::default() } + + rule task_flags() -> TaskFlags + = force_run:force_run() { TaskFlags { force_run } } + + rule force_run() -> bool + = "+" { true } + / { false } + + rule name() -> &'input str + = $(['a'..='z' | 'A' ..='Z' | '0'..='9' | '_' | '-']+) + + rule tagged<T>(tag: rule<()>, value: rule<T>) -> T + = tag() v:value() { v } + } +} diff --git a/crates/rebel-parse/src/grammar/tokenize.rs b/crates/rebel-parse/src/grammar/tokenize.rs new file mode 100644 index 0000000..841e61b --- /dev/null +++ b/crates/rebel-parse/src/grammar/tokenize.rs @@ -0,0 +1,104 @@ +use crate::token::*; + +pub use rules::*; + +peg::parser! { + pub grammar rules() for str { + pub rule token_stream() -> TokenStream<'input> + = _ tokens:(token() ** _) _ { TokenStream(tokens) } + + pub rule token() -> Token<'input> + = number:number() { Token::Number(number) } + / string:string() { Token::String(string) } + / ident:ident() { Token::Ident(ident) } + / punct:punct() { Token::Punct(punct) } + + rule ident() -> &'input str + = $( + ['a'..='z' | 'A' ..='Z' | '_' ] + ['a'..='z' | 'A' ..='Z' | '_' | '0'..='9']* + ) + + rule punct() -> Punct + = ch:punct_char() spacing:spacing() { Punct(ch, spacing) } + + rule punct_char() -> char + = !number() !string() !ident() !__ ch:[_] { ch } + + rule spacing() -> Spacing + = &punct_char() { Spacing::Joint } + / { Spacing::Alone } + + rule number() -> &'input str + = $(['0'..='9'] ['0'..='9' | 'a'..='z' | 'A'..='Z' | '_']*) + + rule string() -> String<'input> + = "\"" pieces:string_piece()* "\"" { + String { + pieces, + kind: StringKind::String, + } + } + / "r\"" chars:$([^'"']*) "\"" { + String { + pieces: vec![StringPiece::Chars(chars)], + kind: StringKind::RawString, + } + } + / "```" newline() pieces:script_string_piece()* "```" { + String { + pieces, + kind: StringKind::ScriptString, + } + } + + rule string_piece() -> StringPiece<'input> + = chars:$((!"{{" [^'"' | '\\'])+) { StringPiece::Chars(chars) } + / "\\" escape:string_escape() { StringPiece::Escape(escape) } + / string_interp() + + rule string_escape() -> char + = "n" { '\n' } + / "r" { '\r' } + / "t" { '\t' } + / "\\" { '\\' } + / "\"" { '"' } + / "0" { '\0' } + / "x" digits:$(['0'..='7'] hex_digit()) { + u8::from_str_radix(digits, 16).unwrap().into() + } + / "u{" digits:$(hex_digit()*<1,6>) "}" { ? + u32::from_str_radix(digits, 16).unwrap().try_into().or(Err("Invalid unicode escape")) + } + + rule script_string_piece() -> StringPiece<'input> + = chars:$((!"{{" !"```" [_])+) { StringPiece::Chars(chars) } + / string_interp() + + rule string_interp() -> StringPiece<'input> + = "{{" _ tokens:(subtoken() ++ _) _ "}}" { + StringPiece::Interp(TokenStream(tokens)) + } + + rule subtoken() -> Token<'input> + = !"}}" token:token() { token } + + rule hex_digit() + = ['0'..='9' | 'a'..='f' | 'A'..='F'] + + /// Mandatory whitespace + rule __ + = ([' ' | '\t'] / quiet!{newline()} / quiet!{comment()})+ + + /// Optional whitespace + rule _ + = quiet!{__?} + + rule comment() + = "//" (!newline() [_])* (newline() / ![_]) + / "/*" (!"*/" [_])* "*/" + + rule newline() + = ['\n' | '\r'] + } +} diff --git a/crates/rebel-parse/src/lib.rs b/crates/rebel-parse/src/lib.rs new file mode 100644 index 0000000..4a8c431 --- /dev/null +++ b/crates/rebel-parse/src/lib.rs @@ -0,0 +1,8 @@ +pub mod ast; +pub mod token; + +mod grammar; + +pub use grammar::recipe; +pub use grammar::task_ref; +pub use grammar::tokenize; diff --git a/crates/rebel-parse/src/token.rs b/crates/rebel-parse/src/token.rs new file mode 100644 index 0000000..2f2f849 --- /dev/null +++ b/crates/rebel-parse/src/token.rs @@ -0,0 +1,68 @@ +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Token<'a> { + Ident(&'a str), + Punct(Punct), + String(String<'a>), + Number(&'a str), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Punct(pub char, pub Spacing); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Spacing { + Alone, + Joint, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct String<'a> { + pub pieces: Vec<StringPiece<'a>>, + pub kind: StringKind, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum StringPiece<'a> { + Chars(&'a str), + Escape(char), + Interp(TokenStream<'a>), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StringKind { + String, + RawString, + ScriptString, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TokenStream<'a>(pub Vec<Token<'a>>); + +impl<'a> peg::Parse for TokenStream<'a> { + type PositionRepr = usize; + + fn start(&self) -> usize { + 0 + } + + fn is_eof(&self, pos: usize) -> bool { + pos >= self.0.len() + } + + fn position_repr(&self, pos: usize) -> Self::PositionRepr { + pos + } +} + +impl<'input, 'a: 'input> peg::ParseElem<'input> for TokenStream<'a> { + type Element = &'input Token<'a>; + + fn parse_elem(&'input self, pos: usize) -> peg::RuleResult<Self::Element> { + use peg::RuleResult; + + match self.0[pos..].first() { + Some(c) => RuleResult::Matched(pos + 1, c), + None => RuleResult::Failed, + } + } +} diff --git a/crates/rebel-resolve/Cargo.toml b/crates/rebel-resolve/Cargo.toml new file mode 100644 index 0000000..4b3e113 --- /dev/null +++ b/crates/rebel-resolve/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rebel-resolve" +version = "0.1.0" +authors = ["Matthias Schiffer <mschiffer@universe-factory.net>"] +license = "MIT" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rebel-common = { path = "../rebel-common" } + +deb-version = "0.1.1" +enum-kinds = "0.5.1" +serde = { version = "1", features = ["derive", "rc"] } diff --git a/crates/driver/src/args.rs b/crates/rebel-resolve/src/args.rs index 805646a..805646a 100644 --- a/crates/driver/src/args.rs +++ b/crates/rebel-resolve/src/args.rs diff --git a/crates/driver/src/context.rs b/crates/rebel-resolve/src/context.rs index 9674e5f..996d981 100644 --- a/crates/driver/src/context.rs +++ b/crates/rebel-resolve/src/context.rs @@ -9,15 +9,14 @@ use std::{ result, }; -use common::{ +use rebel_common::{ error::{self, Contextualizable}, string_hash::ArchiveHash, - types::TaskID, + types::TaskIDRef, }; use crate::{ args::*, - parse::{self, TaskFlags}, paths, pin::{self, Pins}, task::*, @@ -32,7 +31,7 @@ pub enum ErrorKind<'a> { #[derive(Debug, Clone, Copy)] pub struct Error<'a> { - pub task: &'a TaskID, + pub task: TaskIDRef<'a>, pub kind: ErrorKind<'a>, } @@ -65,7 +64,7 @@ pub type Result<'a, T> = result::Result<T, Error<'a>>; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct TaskRef<'ctx> { - pub id: &'ctx TaskID, + pub id: TaskIDRef<'ctx>, pub args: Rc<TaskArgs>, } @@ -75,7 +74,7 @@ impl<'ctx> Display for TaskRef<'ctx> { return self.id.fmt(f); } - let pv_arg = match self.args.get("pv") { + let version_arg = match self.args.get("version") { Some(Arg::String(s)) => Some(s), _ => None, }; @@ -89,13 +88,13 @@ impl<'ctx> Display for TaskRef<'ctx> { }; write!(f, "{}", self.id.recipe)?; - if let Some(pv) = pv_arg { - write!(f, "@{}", pv)?; + if let Some(version) = version_arg { + write!(f, "#{}", version)?; } - write!(f, ":{}", self.id.task)?; + write!(f, "::{}", self.id.task)?; if host_arg.is_some() || target_arg.is_some() { - write!(f, "/")?; + write!(f, "@")?; } if let Some(host) = host_arg { @@ -144,12 +143,15 @@ fn platform_relation(args: &TaskArgs, from: &str, to: &str) -> Option<PlatformRe pub struct Context { platforms: HashMap<String, Arg>, globals: TaskArgs, - tasks: HashMap<TaskID, Vec<TaskDef>>, + tasks: HashMap<String, HashMap<String, Vec<TaskDef>>>, rootfs: (ArchiveHash, String), } impl Context { - pub fn new(mut tasks: HashMap<TaskID, Vec<TaskDef>>, pins: Pins) -> error::Result<Self> { + pub fn new( + mut tasks: HashMap<String, HashMap<String, Vec<TaskDef>>>, + pins: Pins, + ) -> error::Result<Self> { let platforms: HashMap<_, _> = [ arg( "build", @@ -212,7 +214,7 @@ impl Context { } fn add_rootfs_tasks( - tasks: &mut HashMap<TaskID, Vec<TaskDef>>, + tasks: &mut HashMap<String, HashMap<String, Vec<TaskDef>>>, provides: Vec<pin::Provides>, globals: &TaskArgs, ) -> error::Result<()> { @@ -255,10 +257,9 @@ impl Context { task_def.priority = i32::MAX; tasks - .entry(TaskID { - recipe: recipe.to_string(), - task: task.to_string(), - }) + .entry(recipe) + .or_default() + .entry(task) .or_default() .push(task_def); } @@ -293,9 +294,12 @@ impl Context { .max_by(|task1, task2| Self::compare_tasks(task1, task2)) } - fn get_with_args<'a>(&self, id: &'a TaskID, args: &TaskArgs) -> Result<'a, &TaskDef> { - self.tasks - .get(id) + fn get_by_ref(&self, id: TaskIDRef) -> Option<&[TaskDef]> { + Some(self.tasks.get(id.recipe)?.get(id.task)?) + } + + fn get_with_args<'a>(&self, id: TaskIDRef<'a>, args: &TaskArgs) -> Result<'a, &TaskDef> { + self.get_by_ref(id) .and_then(|tasks| Self::select_task(tasks, args)) .ok_or(Error { task: id, @@ -307,7 +311,7 @@ impl Context { self.get_with_args(task.id, task.args.as_ref()) } - fn task_ref<'ctx>(&'ctx self, id: &'ctx TaskID, args: &TaskArgs) -> Result<TaskRef> { + fn task_ref<'ctx>(&'ctx self, id: TaskIDRef<'ctx>, args: &TaskArgs) -> Result<TaskRef> { let task_def = self.get_with_args(id, args)?; let mut arg_def: HashMap<_, _> = task_def.args.iter().map(|(k, &v)| (k, v)).collect(); @@ -345,8 +349,11 @@ impl Context { new_args.set("cross_compile", cross_compile); - new_args.set("pn", Some(task_def.meta.name.clone())); - new_args.set("pv", task_def.meta.version.clone()); + new_args.set("basename", Some(task_def.meta.basename.clone())); + new_args.set("recipename", Some(task_def.meta.recipename.clone())); + new_args.set("recipe", Some(task_def.meta.recipe.clone())); + new_args.set("name", Some(task_def.meta.name.clone())); + new_args.set("version", task_def.meta.version.clone()); Ok(TaskRef { id, @@ -354,21 +361,27 @@ impl Context { }) } - pub fn parse(&self, s: &str) -> error::Result<(TaskRef, TaskFlags)> { - let (parsed, flags) = parse::parse_task_with_flags(s).context("Invalid task syntax")?; - - let recipe = parsed.recipe.to_string(); - let task = parsed.task.to_string(); - - let id = TaskID { recipe, task }; - let (ctx_id, _) = self + pub fn lookup( + &self, + id: TaskIDRef, + host: Option<&str>, + target: Option<&str>, + ) -> error::Result<TaskRef> { + let (ctx_recipe, recipe_tasks) = self .tasks - .get_key_value(&id) + .get_key_value(id.recipe) .with_context(|| format!("Task {} not found", id))?; + let (ctx_task, _) = recipe_tasks + .get_key_value(id.task) + .with_context(|| format!("Task {} not found", id))?; + let ctx_id = TaskIDRef { + recipe: ctx_recipe, + task: ctx_task, + }; let mut args = self.globals.clone(); - if let Some(host) = parsed.host { + if let Some(host) = host { let plat = self .platforms .get(host) @@ -376,7 +389,7 @@ impl Context { args.set("host", Some(plat)); args.set("target", Some(plat)); } - if let Some(target) = parsed.target { + if let Some(target) = target { let plat = self .platforms .get(target) @@ -388,11 +401,11 @@ impl Context { .task_ref(ctx_id, &args) .with_context(|| format!("Failed to instantiate task {}", id))?; - Ok((task_ref, flags)) + Ok(task_ref) } fn map_args<'ctx, 'args>( - task: &'ctx TaskID, + task: TaskIDRef<'ctx>, mapping: &'ctx ArgMapping, args: &'args TaskArgs, build_dep: bool, @@ -419,37 +432,44 @@ impl Context { Ok(Cow::Owned(ret)) } - fn inherit_ref<'ctx>(&'ctx self, dep: &'ctx InheritDep, args: &TaskArgs) -> Result<TaskRef> { - let mapped_args = Context::map_args(&dep.dep.id, &dep.dep.args, args, false)?; - self.task_ref(&dep.dep.id, mapped_args.as_ref()) + fn parent_ref<'ctx>( + &'ctx self, + dep_of: TaskIDRef<'ctx>, + dep: &'ctx ParentDep, + args: &TaskArgs, + ) -> Result<TaskRef> { + let id = dep.dep.id(dep_of.recipe); + let mapped_args = Context::map_args(id, &dep.dep.args, args, false)?; + self.task_ref(id, mapped_args.as_ref()) } pub fn output_ref<'ctx>( &'ctx self, + dep_of: TaskIDRef<'ctx>, dep: &'ctx OutputDep, args: &TaskArgs, build_dep: bool, ) -> Result<OutputRef<'ctx>> { - let mapped_args = Context::map_args(&dep.dep.id, &dep.dep.args, args, build_dep)?; + let id = dep.dep.id(dep_of.recipe); + let mapped_args = Context::map_args(id, &dep.dep.args, args, build_dep)?; Ok(OutputRef { - task: self.task_ref(&dep.dep.id, mapped_args.as_ref())?, + task: self.task_ref(id, mapped_args.as_ref())?, output: &dep.output, }) } - pub fn get_inherit_depend<'ctx>( + pub fn get_parent_depend<'ctx>( &'ctx self, task_ref: &TaskRef<'ctx>, ) -> Result<Option<TaskRef>> { let task = self.get(task_ref)?; - let inherit = match &task.inherit { - Some(inherit) => inherit, - None => return Ok(None), + let Some(parent) = &task.parent else { + return Ok(None); }; - Some(self.inherit_ref(inherit, &task_ref.args)).transpose() + Some(self.parent_ref(task_ref.id, parent, &task_ref.args)).transpose() } - fn inherit_iter<'ctx>( + fn ancestor_iter<'ctx>( &'ctx self, task_ref: &TaskRef<'ctx>, ) -> impl Iterator<Item = Result<TaskRef>> { @@ -463,7 +483,7 @@ impl Context { Ok(task_ref) => task_ref, Err(err) => return Some(Err(err)), }; - self.1 = self.0.get_inherit_depend(&task_ref).transpose(); + self.1 = self.0.get_parent_depend(&task_ref).transpose(); Some(Ok(task_ref)) } } @@ -478,14 +498,14 @@ impl Context { let mut ret = HashSet::new(); let mut allow_noinherit = true; - for current in self.inherit_iter(task_ref) { + for current in self.ancestor_iter(task_ref) { let current_ref = current?; let task = self.get(¤t_ref)?; let entries = task .build_depends .iter() .filter(|dep| allow_noinherit || !dep.noinherit) - .map(|dep| self.output_ref(dep, ¤t_ref.args, true)) + .map(|dep| self.output_ref(task_ref.id, dep, ¤t_ref.args, true)) .collect::<Result<Vec<_>>>()?; ret.extend(entries); @@ -502,14 +522,14 @@ impl Context { let mut ret = HashSet::new(); let mut allow_noinherit = true; - for current in self.inherit_iter(task_ref) { + for current in self.ancestor_iter(task_ref) { let current_ref = current?; let task = self.get(¤t_ref)?; let entries = task .depends .iter() .filter(|dep| allow_noinherit || !dep.noinherit) - .map(|dep| self.output_ref(dep, ¤t_ref.args, false)) + .map(|dep| self.output_ref(task_ref.id, dep, ¤t_ref.args, false)) .collect::<Result<Vec<_>>>()?; ret.extend(entries); diff --git a/crates/driver/src/resolve.rs b/crates/rebel-resolve/src/lib.rs index d03f26d..cc44de8 100644 --- a/crates/driver/src/resolve.rs +++ b/crates/rebel-resolve/src/lib.rs @@ -1,13 +1,19 @@ +pub mod args; +pub mod context; +pub mod paths; +pub mod pin; +pub mod task; + use std::collections::{HashMap, HashSet}; use std::fmt; use std::rc::Rc; -use common::types::TaskID; +use rebel_common::types::TaskIDRef; -use crate::args::TaskArgs; -use crate::context::{self, Context, OutputRef, TaskRef}; +use args::TaskArgs; +use context::{Context, OutputRef, TaskRef}; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct DepChain<'ctx>(pub Vec<TaskRef<'ctx>>); impl<'ctx> fmt::Display for DepChain<'ctx> { @@ -38,8 +44,8 @@ impl<'ctx> From<&TaskRef<'ctx>> for DepChain<'ctx> { } } -impl<'ctx> From<&'ctx TaskID> for DepChain<'ctx> { - fn from(id: &'ctx TaskID) -> Self { +impl<'ctx> From<TaskIDRef<'ctx>> for DepChain<'ctx> { + fn from(id: TaskIDRef<'ctx>) -> Self { TaskRef { id, args: Rc::new(TaskArgs::default()), @@ -48,11 +54,14 @@ impl<'ctx> From<&'ctx TaskID> for DepChain<'ctx> { } } +const MAX_ERRORS: usize = 100; + #[derive(Debug)] pub enum ErrorKind<'ctx> { Context(context::Error<'ctx>), OutputNotFound(&'ctx str), DependencyCycle, + TooManyErrors, } #[derive(Debug)] @@ -76,6 +85,13 @@ impl<'ctx> Error<'ctx> { } } + fn too_many_errors() -> Self { + Error { + dep_chain: DepChain::default(), + kind: ErrorKind::TooManyErrors, + } + } + fn extend(&mut self, task: &TaskRef<'ctx>) { self.dep_chain.0.push(task.clone()); } @@ -94,6 +110,9 @@ impl<'ctx> fmt::Display for Error<'ctx> { ErrorKind::DependencyCycle => { write!(f, "Dependency Cycle: ")?; } + ErrorKind::TooManyErrors => { + write!(f, "Too many errors, stopping.")?; + } } dep_chain.fmt(f) } @@ -149,7 +168,7 @@ where let mut errors = Vec::new(); for runtime_dep in &output.runtime_depends { - match ctx.output_ref(runtime_dep, &task.args, false) { + match ctx.output_ref(task.id, runtime_dep, &task.args, false) { Ok(output_ref) => { for mut error in add_dep(ret, ctx, output_ref) { error.extend(task); @@ -201,7 +220,7 @@ pub fn get_dependent_tasks<'ctx>( task_ref: &TaskRef<'ctx>, ) -> Result<HashSet<TaskRef<'ctx>>, Vec<Error<'ctx>>> { Ok(ctx - .get_inherit_depend(task_ref) + .get_parent_depend(task_ref) .map_err(|err| vec![err.into()])? .into_iter() .chain( @@ -254,33 +273,43 @@ impl<'ctx> Resolver<'ctx> { .insert(task.clone(), ResolveState::Resolving); let mut ret = Vec::new(); - let mut handle_errors = |errors: Vec<Error<'ctx>>| { + let mut handle_errors = |errors: Vec<Error<'ctx>>| -> Result<(), ()> { for mut error in errors { error.extend(task); ret.push(error); + + if ret.len() > MAX_ERRORS { + ret.push(Error::too_many_errors()); + return Err(()); + } } + Ok(()) }; - match self.ctx.get_inherit_depend(task) { - Ok(Some(inherit)) => { - handle_errors(self.add_task(&inherit, None)); - } - Ok(None) => {} - Err(err) => { - handle_errors(vec![err.into()]); + let _ = (|| -> Result<(), ()> { + match self.ctx.get_parent_depend(task) { + Ok(Some(parent)) => { + handle_errors(self.add_task(&parent, None))?; + } + Ok(None) => {} + Err(err) => { + handle_errors(vec![err.into()])?; + } } - } - match get_dependent_outputs(self.ctx, task) { - Ok(rdeps) => { - for rdep in rdeps { - handle_errors(self.add_task(&rdep.task, Some(rdep.output))); + match get_dependent_outputs(self.ctx, task) { + Ok(rdeps) => { + for rdep in rdeps { + handle_errors(self.add_task(&rdep.task, Some(rdep.output)))?; + } + } + Err(errors) => { + handle_errors(errors)?; } } - Err(errors) => { - handle_errors(errors); - } - } + + Ok(()) + })(); if ret.is_empty() { *self diff --git a/crates/driver/src/paths.rs b/crates/rebel-resolve/src/paths.rs index 274dda1..274dda1 100644 --- a/crates/driver/src/paths.rs +++ b/crates/rebel-resolve/src/paths.rs diff --git a/crates/driver/src/pin.rs b/crates/rebel-resolve/src/pin.rs index 26e445c..bffc940 100644 --- a/crates/driver/src/pin.rs +++ b/crates/rebel-resolve/src/pin.rs @@ -1,8 +1,8 @@ -use std::{collections::HashMap, fs::File, path::Path}; +use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use common::{error::*, string_hash::*}; +use rebel_common::string_hash::*; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Args { @@ -29,11 +29,3 @@ pub struct Pin { } pub type Pins = HashMap<String, Pin>; - -pub fn read_pins<P: AsRef<Path>>(path: P) -> Result<Pins> { - let f = File::open(path)?; - let pins: Pins = serde_yaml::from_reader(f) - .map_err(Error::new) - .context("YAML error")?; - Ok(pins) -} diff --git a/crates/driver/src/task.rs b/crates/rebel-resolve/src/task.rs index df3bc68..1220d45 100644 --- a/crates/driver/src/task.rs +++ b/crates/rebel-resolve/src/task.rs @@ -2,28 +2,26 @@ use std::collections::{HashMap, HashSet}; use serde::Deserialize; -use common::{string_hash::StringHash, types::TaskID}; +use rebel_common::{string_hash::StringHash, types::TaskIDRef}; -use crate::{ - args::{ArgMapping, ArgType, TaskArgs}, - recipe, -}; - -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash, Default)] -pub struct RecipeMeta { - #[serde(default)] - pub name: String, - pub version: Option<String>, -} +use crate::args::{ArgMapping, ArgType, TaskArgs}; #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)] pub struct TaskDep { - #[serde(flatten, deserialize_with = "recipe::deserialize_task_id")] - pub id: TaskID, + pub recipe: Option<String>, + pub task: String, #[serde(default)] pub args: ArgMapping, } +impl TaskDep { + pub fn id<'a>(&'a self, recipe: &'a str) -> TaskIDRef<'a> { + let recipe = self.recipe.as_deref().unwrap_or(recipe); + let task = &self.task; + TaskIDRef { recipe, task } + } +} + #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)] pub struct Fetch { pub name: String, @@ -35,7 +33,7 @@ fn default_output_name() -> String { } #[derive(Clone, Debug, Deserialize)] -pub struct InheritDep { +pub struct ParentDep { #[serde(flatten)] pub dep: TaskDep, } @@ -69,14 +67,23 @@ impl Action { } } +#[derive(Clone, Debug, Default)] +pub struct TaskMeta { + pub basename: String, + pub recipename: String, + pub recipe: String, + pub name: String, + pub version: Option<String>, +} + #[derive(Clone, Debug, Deserialize, Default)] pub struct TaskDef { #[serde(skip)] - pub meta: RecipeMeta, + pub meta: TaskMeta, #[serde(default)] pub args: HashMap<String, ArgType>, #[serde(default)] - pub inherit: Option<InheritDep>, + pub parent: Option<ParentDep>, #[serde(default)] pub fetch: HashSet<Fetch>, #[serde(default)] diff --git a/crates/runner/Cargo.toml b/crates/rebel-runner/Cargo.toml index bd1287e..3df06f8 100644 --- a/crates/runner/Cargo.toml +++ b/crates/rebel-runner/Cargo.toml @@ -8,14 +8,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -common = { path = "../common", package = "rebel-common" } +rebel-common = { path = "../rebel-common" } bincode = "1.3.3" blake3 = { version = "1.3.0", features = ["traits-preview"] } capctl = "0.2.0" digest = "0.10.1" libc = "0.2.84" -nix = { version = "0.27.1", features = ["user", "fs", "process", "mount", "sched", "poll", "signal", "hostname"] } +nix = { version = "0.28.0", features = ["user", "fs", "process", "mount", "sched", "poll", "signal", "hostname", "resource"] } olpc-cjson = "0.1.0" serde = { version = "1", features = ["derive"] } serde_json = "1.0.62" diff --git a/crates/runner/src/init.rs b/crates/rebel-runner/src/init.rs index ede8fd8..0172a01 100644 --- a/crates/runner/src/init.rs +++ b/crates/rebel-runner/src/init.rs @@ -1,6 +1,6 @@ use nix::mount::{self, MsFlags}; -use common::error::*; +use rebel_common::error::*; use crate::{paths, util::fs}; diff --git a/crates/runner/src/jobserver.rs b/crates/rebel-runner/src/jobserver.rs index 2422a75..7c3f2f7 100644 --- a/crates/runner/src/jobserver.rs +++ b/crates/rebel-runner/src/jobserver.rs @@ -1,11 +1,11 @@ use std::{ - os::fd::{AsRawFd, FromRawFd, OwnedFd}, + os::fd::{AsFd, AsRawFd, OwnedFd}, slice, }; use nix::{errno::Errno, fcntl::OFlag, poll, unistd}; -use common::error::*; +use rebel_common::error::*; use super::util::unix; @@ -18,13 +18,10 @@ pub struct Jobserver { impl Jobserver { pub fn new(tokens: usize) -> Result<Jobserver> { - let (piper, pipew) = - unistd::pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK).context("pipe()")?; - let r = unsafe { OwnedFd::from_raw_fd(piper) }; - let w = unsafe { OwnedFd::from_raw_fd(pipew) }; + let (r, w) = unistd::pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK).context("pipe()")?; for _ in 0..tokens { - if unistd::write(w.as_raw_fd(), b"+").is_err() { + if unistd::write(w.as_fd(), b"+").is_err() { break; } } @@ -36,8 +33,8 @@ impl Jobserver { pub fn wait(&mut self) -> u8 { loop { poll::poll( - &mut [poll::PollFd::new(&self.r, poll::PollFlags::POLLIN)], - -1, + &mut [poll::PollFd::new(self.r.as_fd(), poll::PollFlags::POLLIN)], + poll::PollTimeout::NONE, ) .expect("poll()"); @@ -59,7 +56,7 @@ impl Jobserver { } pub fn post(&mut self, token: u8) { - let n = unistd::write(self.w.as_raw_fd(), slice::from_ref(&token)).expect("write()"); + let n = unistd::write(self.w.as_fd(), slice::from_ref(&token)).expect("write()"); assert!(n == 1); } diff --git a/crates/runner/src/lib.rs b/crates/rebel-runner/src/lib.rs index 308b54c..7dde05d 100644 --- a/crates/runner/src/lib.rs +++ b/crates/rebel-runner/src/lib.rs @@ -17,6 +17,7 @@ use std::{ use capctl::prctl; use nix::{ errno::Errno, + fcntl::Flock, poll, sched::CloneFlags, sys::{ @@ -28,7 +29,7 @@ use nix::{ }; use uds::UnixSeqpacketConn; -use common::{error::*, types::*}; +use rebel_common::{error::*, types::*}; use jobserver::Jobserver; use util::{checkable::Checkable, clone, steal::Steal, unix}; @@ -99,7 +100,14 @@ fn borrow_socket_fd(socket: &UnixSeqpacketConn) -> BorrowedFd<'_> { unsafe { BorrowedFd::borrow_raw(socket.as_raw_fd()) } } -fn runner(uid: Uid, gid: Gid, socket: UnixSeqpacketConn, _lockfile: File, options: &Options) -> ! { +fn runner( + uid: Uid, + gid: Gid, + socket: UnixSeqpacketConn, + _lockfile: Flock<File>, + options: &Options, +) -> ! { + unistd::setsid().expect("setsid()"); ns::mount_proc(); ns::setup_userns(Uid::from_raw(0), Gid::from_raw(0), uid, gid); @@ -127,10 +135,10 @@ fn runner(uid: Uid, gid: Gid, socket: UnixSeqpacketConn, _lockfile: File, option loop { let socket_fd = borrow_socket_fd(&ctx.socket); let mut pollfds = [ - poll::PollFd::new(&signal_fd, poll::PollFlags::POLLIN), - poll::PollFd::new(&socket_fd, poll::PollFlags::POLLIN), + poll::PollFd::new(signal_fd.as_fd(), poll::PollFlags::POLLIN), + poll::PollFd::new(socket_fd.as_fd(), poll::PollFlags::POLLIN), ]; - poll::poll(&mut pollfds, -1).expect("poll()"); + poll::poll(&mut pollfds, poll::PollTimeout::NONE).expect("poll()"); let signal_events = pollfds[0] .revents() diff --git a/crates/runner/src/ns.rs b/crates/rebel-runner/src/ns.rs index 4a8e3e7..986aa80 100644 --- a/crates/runner/src/ns.rs +++ b/crates/rebel-runner/src/ns.rs @@ -4,7 +4,7 @@ use nix::{ unistd::{self, Gid, Pid, Uid}, }; -use common::error::*; +use rebel_common::error::*; use super::util::clone; diff --git a/crates/runner/src/paths.rs b/crates/rebel-runner/src/paths.rs index 4b3a126..84f9c4d 100644 --- a/crates/runner/src/paths.rs +++ b/crates/rebel-runner/src/paths.rs @@ -36,7 +36,7 @@ //! └── rootfs/ # rootfs overlay mountpoint //! ``` -use common::string_hash::*; +use rebel_common::string_hash::*; pub const DOWNLOADS_DIR: &str = "build/downloads"; pub const PIN_DIR: &str = "build/pinned"; diff --git a/crates/runner/src/tar.rs b/crates/rebel-runner/src/tar.rs index 1a66408..891c603 100644 --- a/crates/runner/src/tar.rs +++ b/crates/rebel-runner/src/tar.rs @@ -11,7 +11,7 @@ use nix::{ sys::wait, }; -use common::{error::*, string_hash::ArchiveHash}; +use rebel_common::{error::*, string_hash::ArchiveHash}; use super::{ ns, diff --git a/crates/runner/src/task.rs b/crates/rebel-runner/src/task.rs index b716b82..5bb253a 100644 --- a/crates/runner/src/task.rs +++ b/crates/rebel-runner/src/task.rs @@ -11,13 +11,13 @@ use capctl::prctl; use nix::{ mount::{self, MsFlags}, sched::{unshare, CloneFlags}, - sys::wait, + sys::{resource, time::TimeVal, wait}, unistd::{self, Gid, Uid}, }; use serde::Serialize; use tee_readwrite::{TeeReader, TeeWriter}; -use common::{error::*, string_hash::*, types::*}; +use rebel_common::{error::*, string_hash::*, types::*}; use walkdir::WalkDir; use super::{ @@ -52,7 +52,7 @@ fn input_hash(task: &Task) -> InputHash { pub command: &'a str, pub workdir: &'a str, pub rootfs: &'a ArchiveHash, - pub inherit: &'a [LayerHash], + pub ancestors: &'a [LayerHash], pub depends: HashMap<DependencyHash, &'a Dependency>, pub outputs: &'a HashMap<String, String>, } @@ -60,7 +60,7 @@ fn input_hash(task: &Task) -> InputHash { command: &task.command, workdir: &task.workdir, rootfs: &task.rootfs, - inherit: &task.inherit, + ancestors: &task.ancestors, depends: task .depends .iter() @@ -94,7 +94,7 @@ fn init_task(input_hash: &InputHash, task: &Task) -> Result<fs::Mount> { .with_context(|| format!("Failed to write {}", runfile))?; let mount_target = paths::join(&[&task_tmp_dir, &task.workdir]); - let mount = if task.inherit.is_empty() { + let mount = if task.ancestors.is_empty() { fs::mount(task_layer_dir, &mount_target, None, MsFlags::MS_BIND, None) .with_context(|| format!("Failed to bind mount to {:?}", mount_target))? } else { @@ -104,7 +104,7 @@ fn init_task(input_hash: &InputHash, task: &Task) -> Result<fs::Mount> { fs::fixup_permissions(&task_work_dir)?; let lower = task - .inherit + .ancestors .iter() .rev() .map(paths::layer_dir) @@ -452,7 +452,6 @@ fn run_task(input_hash: &InputHash, task: &Task, jobserver: &mut Jobserver) -> R .env_clear() .env("PATH", "/usr/sbin:/usr/bin:/sbin:/bin") .env("HOME", "/build") - .env("INPUT_HASH", input_hash.to_string()) .env("MAKEFLAGS", jobserver.to_makeflags()) .exec(); eprintln!("{}", err); @@ -569,6 +568,26 @@ fn save_cached(input_hash: &InputHash, output: &TaskOutput) -> Result<()> { Ok(()) } +trait AsSecsF32 { + fn as_secs_f32(&self) -> f32; +} + +impl AsSecsF32 for TimeVal { + fn as_secs_f32(&self) -> f32 { + self.tv_sec() as f32 + 1e-6 * (self.tv_usec() as f32) + } +} + +fn get_usage(total: f32) -> String { + let usage = resource::getrusage(resource::UsageWho::RUSAGE_CHILDREN).expect("getrusage()"); + + let user = usage.user_time().as_secs_f32(); + let system = usage.system_time().as_secs_f32(); + let cpu = (100.0 * (user + system) / total).round(); + + format!("{user:.2}s user {system:.2}s system {cpu:.0}% cpu") +} + pub fn handle(task: Task, jobserver: &mut Jobserver) -> Result<TaskOutput> { let input_hash = input_hash(&task); @@ -597,12 +616,11 @@ pub fn handle(task: Task, jobserver: &mut Jobserver) -> Result<TaskOutput> { save_cached(&input_hash, &task_output)?; - let duration = Instant::now().duration_since(start_time); + let duration = Instant::now().duration_since(start_time).as_secs_f32(); + let usage = get_usage(duration); println!( - "Finished task {} ({}) in {}", - task.label, - input_hash, - duration.as_secs_f32() + "Finished task {} ({}) in {:.2}s ({})", + task.label, input_hash, duration, usage, ); if let Ok(cached_output) = cached_output { diff --git a/crates/runner/src/util/checkable.rs b/crates/rebel-runner/src/util/checkable.rs index 8528d29..8528d29 100644 --- a/crates/runner/src/util/checkable.rs +++ b/crates/rebel-runner/src/util/checkable.rs diff --git a/crates/runner/src/util/cjson.rs b/crates/rebel-runner/src/util/cjson.rs index e3840ce..e3840ce 100644 --- a/crates/runner/src/util/cjson.rs +++ b/crates/rebel-runner/src/util/cjson.rs diff --git a/crates/runner/src/util/clone.rs b/crates/rebel-runner/src/util/clone.rs index 51a31c3..51a31c3 100644 --- a/crates/runner/src/util/clone.rs +++ b/crates/rebel-runner/src/util/clone.rs diff --git a/crates/runner/src/util/fs.rs b/crates/rebel-runner/src/util/fs.rs index 9e16648..9e33eb7 100644 --- a/crates/runner/src/util/fs.rs +++ b/crates/rebel-runner/src/util/fs.rs @@ -11,7 +11,7 @@ use nix::{ unistd, }; -use common::error::*; +use rebel_common::error::*; pub fn open<P: AsRef<Path>>(path: P) -> Result<fs::File> { fs::File::open(path.as_ref()) @@ -123,5 +123,5 @@ pub fn mount<P1: AsRef<Path>, P2: AsRef<Path>>( pub fn pipe() -> Result<(File, File)> { unistd::pipe2(OFlag::O_CLOEXEC) .context("pipe2()") - .map(|(piper, pipew)| unsafe { (File::from_raw_fd(piper), File::from_raw_fd(pipew)) }) + .map(|(piper, pipew)| (File::from(piper), File::from(pipew))) } diff --git a/crates/runner/src/util/mod.rs b/crates/rebel-runner/src/util/mod.rs index 0fbe3b5..0fbe3b5 100644 --- a/crates/runner/src/util/mod.rs +++ b/crates/rebel-runner/src/util/mod.rs diff --git a/crates/runner/src/util/stack.rs b/crates/rebel-runner/src/util/stack.rs index 15d5daf..15d5daf 100644 --- a/crates/runner/src/util/stack.rs +++ b/crates/rebel-runner/src/util/stack.rs diff --git a/crates/runner/src/util/steal.rs b/crates/rebel-runner/src/util/steal.rs index 91b2cdf..91b2cdf 100644 --- a/crates/runner/src/util/steal.rs +++ b/crates/rebel-runner/src/util/steal.rs diff --git a/crates/runner/src/util/unix.rs b/crates/rebel-runner/src/util/unix.rs index 156e441..a97b1db 100644 --- a/crates/runner/src/util/unix.rs +++ b/crates/rebel-runner/src/util/unix.rs @@ -1,12 +1,12 @@ use std::{fs::File, os::unix::prelude::*, path::Path}; use nix::{ - fcntl::{self, FcntlArg, FdFlag, OFlag}, + fcntl::{self, FcntlArg, FdFlag, Flock, OFlag}, sched, unistd::Pid, }; -use common::error::*; +use rebel_common::error::*; use super::fs; @@ -65,7 +65,7 @@ pub fn nproc() -> Result<usize> { Ok(count) } -pub fn lock<P: AsRef<Path>>(path: P, exclusive: bool, blocking: bool) -> Result<File> { +pub fn lock<P: AsRef<Path>>(path: P, exclusive: bool, blocking: bool) -> Result<Flock<File>> { use fcntl::FlockArg::*; if let Some(parent) = path.as_ref().parent() { @@ -80,8 +80,7 @@ pub fn lock<P: AsRef<Path>>(path: P, exclusive: bool, blocking: bool) -> Result< }; let file = fs::create(path.as_ref())?; - fcntl::flock(file.as_raw_fd(), arg) - .with_context(|| format!("flock failed on {:?}", path.as_ref()))?; - - Ok(file) + fcntl::Flock::lock(file, arg) + .map_err(|(_, errno)| errno) + .with_context(|| format!("flock failed on {:?}", path.as_ref())) } diff --git a/crates/driver/Cargo.toml b/crates/rebel/Cargo.toml index ecea8b8..9eba0fa 100644 --- a/crates/driver/Cargo.toml +++ b/crates/rebel/Cargo.toml @@ -8,18 +8,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -common = { path = "../common", package = "rebel-common" } -runner = { path = "../runner", package = "rebel-runner" } +rebel-common = { path = "../rebel-common" } +rebel-parse = { path = "../rebel-parse" } +rebel-resolve = { path = "../rebel-resolve" } +rebel-runner = { path = "../rebel-runner" } clap = { version = "4.0.0", features = ["derive"] } -deb-version = "0.1.1" -enum-kinds = "0.5.1" -handlebars = "4.1.3" +handlebars = "5.1.2" indoc = "2.0.4" lazy_static = "1.4.0" -nix = { version = "0.27.1", features = ["poll"] } -nom = "7.1.0" -scoped-tls-hkt = "0.1.2" -serde = { version = "1", features = ["derive", "rc"] } +nix = { version = "0.28.0", features = ["poll", "signal"] } +serde = { version = "1", features = ["derive"] } serde_yaml = "0.9" walkdir = "2" diff --git a/crates/rebel/src/driver.rs b/crates/rebel/src/driver.rs new file mode 100644 index 0000000..e4de2a7 --- /dev/null +++ b/crates/rebel/src/driver.rs @@ -0,0 +1,481 @@ +use std::{ + collections::{HashMap, HashSet}, + iter, + os::unix::{net::UnixStream, prelude::*}, +}; + +use indoc::indoc; +use nix::{ + poll, + sys::{ + signal, + signalfd::{SfdFlags, SignalFd}, + }, +}; + +use rebel_common::{error::*, string_hash::*, types::*}; +use rebel_resolve::{ + self as resolve, + context::{Context, OutputRef, TaskRef}, + paths, + task::*, +}; +use rebel_runner::Runner; + +use crate::template; + +#[derive(Debug)] +pub struct CompletionState<'ctx> { + ctx: &'ctx Context, + tasks_done: HashMap<TaskRef<'ctx>, TaskOutput>, +} + +impl<'ctx> CompletionState<'ctx> { + pub fn new(ctx: &'ctx Context) -> Self { + CompletionState { + ctx, + tasks_done: Default::default(), + } + } + + // Treats both "depends" and "parent" as dependencies + fn deps_satisfied(&self, task_ref: &TaskRef) -> bool { + resolve::get_dependent_tasks(self.ctx, task_ref) + .map_err(|_| Error::new(format!("invalid dependency for {}", task_ref))) + .unwrap() + .into_iter() + .all(|dep| self.tasks_done.contains_key(&dep)) + } + + fn fetch_deps(&self, task: &TaskRef<'ctx>) -> Result<Vec<Dependency>> { + let task_def = &self.ctx[task]; + task_def + .fetch + .iter() + .map(|Fetch { name, sha256 }| { + Ok(Dependency::Fetch { + name: template::ENGINE.eval(name, &task.args).with_context(|| { + format!("Failed to evaluate fetch filename for task {}", task) + })?, + target_dir: paths::TASK_DLDIR.to_string(), + sha256: *sha256, + }) + }) + .collect() + } + + fn dep_closure<I>(&self, deps: I, path: &'ctx str) -> impl Iterator<Item = Dependency> + '_ + where + I: IntoIterator<Item = OutputRef<'ctx>>, + { + resolve::runtime_depends(self.ctx, deps) + .expect("invalid runtime depends") + .into_iter() + .filter_map(|dep| self.tasks_done[&dep.task].outputs.get(dep.output)) + .map(|&output| Dependency::Task { + output, + path: path.to_string(), + }) + } + + fn build_deps(&self, task: &TaskRef<'ctx>) -> Result<impl Iterator<Item = Dependency> + '_> { + Ok(self.dep_closure( + self.ctx + .get_build_depends(task) + .with_context(|| format!("invalid build depends for {}", task))?, + "", + )) + } + + fn host_deps(&self, task: &TaskRef<'ctx>) -> Result<impl Iterator<Item = Dependency> + '_> { + Ok(self.dep_closure( + self.ctx + .get_host_depends(task) + .with_context(|| format!("invalid depends for {}", task))?, + paths::TASK_SYSROOT, + )) + } + + fn task_deps(&self, task: &TaskRef<'ctx>) -> Result<HashSet<Dependency>> { + let fetch_deps = self.fetch_deps(task)?.into_iter(); + let build_deps = self.build_deps(task)?; + let host_deps = self.host_deps(task)?; + + Ok(fetch_deps.chain(build_deps).chain(host_deps).collect()) + } + + fn task_ancestors(&self, task_ref: &TaskRef<'ctx>) -> Vec<LayerHash> { + let Some(parent) = self + .ctx + .get_parent_depend(task_ref) + .expect("invalid parent depends") + else { + return vec![]; + }; + + let mut chain = self.task_ancestors(&parent); + if let Some(layer) = self.tasks_done[&parent].layer { + chain.push(layer); + } + chain + } + + fn print_summary(&self) { + println!(); + println!("Summary:"); + + let mut tasks: Box<[_]> = self.tasks_done.iter().collect(); + tasks.sort_by_cached_key(|(task, _)| format!("{:#}", task)); + for (task_ref, task) in tasks.iter() { + println!(); + println!("{:#}", task_ref); + if let Some(hash) = task.input_hash { + println!(" input: {}", hash); + } + if let Some(hash) = task.layer { + println!(" layer: {}", hash); + } + if !task.outputs.is_empty() { + println!(" outputs:"); + + let mut outputs: Box<[_]> = task.outputs.iter().collect(); + outputs.sort_by_key(|(output, _)| *output); + for (output, hash) in outputs.iter() { + println!(" {}: {}", output, hash); + } + } + } + } +} + +#[derive(Debug)] +enum SpawnResult { + Spawned(UnixStream), + Skipped(TaskOutput), +} + +#[derive(Debug, PartialEq, Eq, Hash)] +enum TaskWaitResult { + Failed, + Interrupted, +} + +#[derive(Debug)] +pub struct Driver<'ctx> { + rdeps: HashMap<TaskRef<'ctx>, Vec<TaskRef<'ctx>>>, + force_run: HashSet<TaskRef<'ctx>>, + tasks_blocked: HashSet<TaskRef<'ctx>>, + tasks_runnable: Vec<TaskRef<'ctx>>, + tasks_running: HashMap<RawFd, (UnixStream, TaskRef<'ctx>)>, + state: CompletionState<'ctx>, +} + +impl<'ctx> Driver<'ctx> { + pub fn new( + ctx: &'ctx Context, + taskset: HashSet<TaskRef<'ctx>>, + force_run: HashSet<TaskRef<'ctx>>, + ) -> Result<Self> { + let mut driver = Driver { + rdeps: Default::default(), + force_run, + tasks_blocked: Default::default(), + tasks_runnable: Default::default(), + tasks_running: Default::default(), + state: CompletionState::new(ctx), + }; + + for task in taskset { + let mut has_depends = false; + for dep in resolve::get_dependent_tasks(ctx, &task) + .map_err(|_| Error::new(format!("invalid dependency for {}", task)))? + { + let rdep = driver.rdeps.entry(dep.clone()).or_default(); + rdep.push(task.clone()); + has_depends = true; + } + + if has_depends { + driver.tasks_blocked.insert(task); + } else { + driver.tasks_runnable.push(task); + } + } + + Ok(driver) + } + + const PREAMBLE: &'static str = indoc! {" + export PATH={{build.prefix}}/sbin:{{build.prefix}}/bin:$PATH + cd {{workdir}} + + export SOURCE_DATE_EPOCH=1 + + export AR_FOR_BUILD=ar + export AS_FOR_BUILD=as + export DLLTOOL_FOR_BUILD=dlltool + export CC_FOR_BUILD=gcc + export CXX_FOR_BUILD=g++ + export GCC_FOR_BUILD=gcc + export GFORTRAN_FOR_BUILD=gfortran + export GOC_FOR_BUILD=goc + export LD_FOR_BUILD=ld + export LIPO_FOR_BUILD=lipo + export NM_FOR_BUILD=nm + export OBJCOPY_FOR_BUILD=objcopy + export OBJDUMP_FOR_BUILD=objdump + export RANLIB_FOR_BUILD=ranlib + export STRIP_FOR_BUILD=strip + export WINDRES_FOR_BUILD=windres + export WINDMC_FOR_BUILD=windmc + "}; + const PREAMBLE_HOST: &'static str = indoc! {" + export AR={{build_to_host.cross_compile}}ar + export AS={{build_to_host.cross_compile}}as + export DLLTOOL={{build_to_host.cross_compile}}dlltool + export CC={{build_to_host.cross_compile}}gcc + export CXX={{build_to_host.cross_compile}}g++ + export GCC={{build_to_host.cross_compile}}gcc + export GFORTRAN={{build_to_host.cross_compile}}gfortran + export GOC={{build_to_host.cross_compile}}goc + export LD={{build_to_host.cross_compile}}ld + export LIPO={{build_to_host.cross_compile}}lipo + export NM={{build_to_host.cross_compile}}nm + export OBJCOPY={{build_to_host.cross_compile}}objcopy + export OBJDUMP={{build_to_host.cross_compile}}objdump + export RANLIB={{build_to_host.cross_compile}}ranlib + export STRIP={{build_to_host.cross_compile}}strip + export WINDRES={{build_to_host.cross_compile}}windres + export WINDMC={{build_to_host.cross_compile}}windmc + "}; + const PREAMBLE_TARGET: &'static str = indoc! {" + export AR_FOR_TARGET={{build_to_target.cross_compile}}ar + export AS_FOR_TARGET={{build_to_target.cross_compile}}as + export DLLTOOL_FOR_TARGET={{build_to_target.cross_compile}}dlltool + export CC_FOR_TARGET={{build_to_target.cross_compile}}gcc + export CXX_FOR_TARGET={{build_to_target.cross_compile}}g++ + export GCC_FOR_TARGET={{build_to_target.cross_compile}}gcc + export GFORTRAN_FOR_TARGET={{build_to_target.cross_compile}}gfortran + export GOC_FOR_TARGET={{build_to_target.cross_compile}}goc + export LD_FOR_TARGET={{build_to_target.cross_compile}}ld + export LIPO_FOR_TARGET={{build_to_target.cross_compile}}lipo + export NM_FOR_TARGET={{build_to_target.cross_compile}}nm + export OBJCOPY_FOR_TARGET={{build_to_target.cross_compile}}objcopy + export OBJDUMP_FOR_TARGET={{build_to_target.cross_compile}}objdump + export RANLIB_FOR_TARGET={{build_to_target.cross_compile}}ranlib + export STRIP_FOR_TARGET={{build_to_target.cross_compile}}strip + export WINDRES_FOR_TARGET={{build_to_target.cross_compile}}windres + export WINDMC_FOR_TARGET={{build_to_target.cross_compile}}windmc + "}; + + fn task_preamble(task_ref: &TaskRef<'ctx>) -> Vec<&'static str> { + let mut ret = vec![Self::PREAMBLE]; + + if task_ref.args.contains_key("build_to_host") { + ret.push(Self::PREAMBLE_HOST); + } + if task_ref.args.contains_key("build_to_target") { + ret.push(Self::PREAMBLE_TARGET); + } + ret + } + + fn update_runnable(&mut self, task_ref: TaskRef<'ctx>, task_output: TaskOutput) { + let rdeps = self.rdeps.get(&task_ref); + + self.state.tasks_done.insert(task_ref, task_output); + + for rdep in rdeps.unwrap_or(&Vec::new()) { + if !self.tasks_blocked.contains(rdep) { + continue; + } + if self.state.deps_satisfied(rdep) { + self.tasks_blocked.remove(rdep); + self.tasks_runnable.push(rdep.clone()); + } + } + } + + fn spawn_task(&self, task_ref: &TaskRef<'ctx>, runner: &Runner) -> Result<SpawnResult> { + let task_def = &self.state.ctx[task_ref]; + if task_def.action.is_empty() { + println!("Skipping empty task {:#}", task_ref); + return Ok(SpawnResult::Skipped(TaskOutput::default())); + } + + let task_deps = self.state.task_deps(task_ref)?; + let task_output = task_def + .output + .iter() + .map(|(name, Output { path, .. })| { + let output_path = if let Some(path) = path { + format!("{}/{}", paths::TASK_DESTDIR, path) + } else { + paths::TASK_DESTDIR.to_string() + }; + (name.clone(), output_path) + }) + .collect(); + + let ancestors = self.state.task_ancestors(task_ref); + + let mut run = Self::task_preamble(task_ref); + run.push(&task_def.action.run); + + let command = template::ENGINE + .eval_sh(&run.concat(), &task_ref.args) + .with_context(|| { + format!("Failed to evaluate command template for task {}", task_ref) + })?; + + let rootfs = self.state.ctx.get_rootfs(); + let task = Task { + label: format!("{:#}", task_ref), + command, + workdir: paths::TASK_WORKDIR.to_string(), + rootfs: rootfs.0, + ancestors, + depends: task_deps, + outputs: task_output, + pins: HashMap::from([rootfs.clone()]), + force_run: self.force_run.contains(task_ref), + }; + + Ok(SpawnResult::Spawned(runner.spawn(&task))) + } + + fn run_task(&mut self, task_ref: TaskRef<'ctx>, runner: &Runner) -> Result<()> { + match self.spawn_task(&task_ref, runner)? { + SpawnResult::Spawned(socket) => { + assert!(self + .tasks_running + .insert(socket.as_raw_fd(), (socket, task_ref)) + .is_none()); + } + SpawnResult::Skipped(result) => { + self.update_runnable(task_ref, result); + } + } + Ok(()) + } + + fn run_tasks(&mut self, runner: &Runner) -> Result<()> { + while let Some(task_ref) = self.tasks_runnable.pop() { + self.run_task(task_ref, runner)?; + } + Ok(()) + } + + fn wait_for_task(&mut self, signal_fd: &mut SignalFd) -> Result<Option<TaskWaitResult>> { + let mut pollfds: Vec<_> = self + .tasks_running + .values() + .map(|(socket, _)| socket.as_fd()) + .chain(iter::once(signal_fd.as_fd())) + .map(|fd| poll::PollFd::new(fd, poll::PollFlags::POLLIN)) + .collect(); + + while poll::poll(&mut pollfds, poll::PollTimeout::NONE).context("poll()")? == 0 {} + + let pollevents: Vec<_> = pollfds + .into_iter() + .map(|pollfd| { + ( + pollfd.as_fd().as_raw_fd(), + pollfd.revents().expect("Unknown events in poll() return"), + ) + }) + .collect(); + + for (fd, events) in pollevents { + if !events.contains(poll::PollFlags::POLLIN) { + if events.intersects(!poll::PollFlags::POLLIN) { + return Err(Error::new( + "Unexpected error status for socket file descriptor", + )); + } + continue; + } + + if fd == signal_fd.as_raw_fd() { + let _signal = signal_fd.read_signal().expect("read_signal()").unwrap(); + return Ok(Some(TaskWaitResult::Interrupted)); + } + + let (socket, task_ref) = self.tasks_running.remove(&fd).unwrap(); + + match Runner::result(&socket) { + Ok(task_output) => { + self.update_runnable(task_ref, task_output); + } + Err(error) => { + eprintln!("{}", error); + return Ok(Some(TaskWaitResult::Failed)); + } + } + } + + Ok(None) + } + + fn is_done(&self) -> bool { + self.tasks_blocked.is_empty() + && self.tasks_runnable.is_empty() + && self.tasks_running.is_empty() + } + + fn setup_signalfd() -> Result<SignalFd> { + let mut signals = signal::SigSet::empty(); + signals.add(signal::Signal::SIGINT); + signal::pthread_sigmask(signal::SigmaskHow::SIG_BLOCK, Some(&signals), None) + .expect("pthread_sigmask()"); + SignalFd::with_flags(&signals, SfdFlags::SFD_CLOEXEC) + .context("Failed to create signal file descriptor") + } + + fn raise_sigint() { + let mut signals = signal::SigSet::empty(); + signals.add(signal::Signal::SIGINT); + signal::pthread_sigmask(signal::SigmaskHow::SIG_UNBLOCK, Some(&signals), None) + .expect("pthread_sigmask()"); + signal::raise(signal::Signal::SIGINT).expect("raise()"); + unreachable!(); + } + + pub fn run(&mut self, runner: &Runner, keep_going: bool) -> Result<bool> { + let mut success = true; + let mut interrupted = false; + + let mut signal_fd = Self::setup_signalfd()?; + + self.run_tasks(runner)?; + + while !self.tasks_running.is_empty() { + match self.wait_for_task(&mut signal_fd)? { + Some(TaskWaitResult::Failed) => { + success = false; + } + Some(TaskWaitResult::Interrupted) => { + if interrupted { + Self::raise_sigint(); + } + eprintln!("Interrupt received, not spawning new tasks. Interrupt again to stop immediately."); + interrupted = true; + } + None => {} + } + if !interrupted && (success || keep_going) { + self.run_tasks(runner)?; + } + } + + if interrupted || !success { + return Ok(false); + } + + assert!(self.is_done(), "No runnable tasks left"); + self.state.print_summary(); + + Ok(true) + } +} diff --git a/crates/driver/src/main.rs b/crates/rebel/src/main.rs index 98aa10a..625b43d 100644 --- a/crates/driver/src/main.rs +++ b/crates/rebel/src/main.rs @@ -1,19 +1,15 @@ -mod args; -mod context; mod driver; -mod parse; -mod paths; -mod pin; mod recipe; -mod resolve; -mod task; mod template; -use std::collections::HashSet; +use std::{collections::HashSet, fs::File, path::Path}; use clap::Parser; -use runner::Runner; +use rebel_common::error::*; +use rebel_parse as parse; +use rebel_resolve::{self as resolve, context, pin}; +use rebel_runner::{self as runner, Runner}; #[derive(Parser)] #[clap(version, about)] @@ -22,11 +18,22 @@ struct Opts { /// Defaults to the number of available CPUs #[clap(short, long)] jobs: Option<usize>, + /// Keep going after some tasks have failed + #[clap(short, long)] + keep_going: bool, /// The tasks to run #[clap(name = "task", required = true)] tasks: Vec<String>, } +fn read_pins<P: AsRef<Path>>(path: P) -> Result<pin::Pins> { + let f = File::open(path)?; + let pins: pin::Pins = serde_yaml::from_reader(f) + .map_err(Error::new) + .context("YAML error")?; + Ok(pins) +} + fn main() { let opts: Opts = Opts::parse(); @@ -34,7 +41,7 @@ fn main() { let ctx = context::Context::new( recipe::read_recipes("examples/recipes").unwrap(), - pin::read_pins("examples/pins.yml").unwrap(), + read_pins("examples/pins.yml").unwrap(), ) .unwrap(); @@ -42,7 +49,11 @@ fn main() { let mut force_run = HashSet::new(); for task in opts.tasks { - let (task_ref, flags) = match ctx.parse(&task) { + let Ok((parsed, flags)) = parse::task_ref::task_ref_with_flags(&task) else { + eprintln!("Invalid task syntax"); + std::process::exit(1); + }; + let task_ref = match ctx.lookup(parsed.id, parsed.args.host, parsed.args.target) { Ok(task_ref) => task_ref, Err(err) => { eprintln!("{}", err); @@ -62,8 +73,15 @@ fn main() { } let taskset = rsv.into_taskset(); let mut driver = driver::Driver::new(&ctx, taskset, force_run).unwrap(); - if let Err(error) = driver.run(&runner) { - eprintln!("{}", error); - std::process::exit(1); + match driver.run(&runner, opts.keep_going) { + Ok(success) => { + if !success { + std::process::exit(1); + } + } + Err(error) => { + eprintln!("{}", error); + std::process::exit(1); + } } } diff --git a/crates/rebel/src/recipe.rs b/crates/rebel/src/recipe.rs new file mode 100644 index 0000000..28cc84c --- /dev/null +++ b/crates/rebel/src/recipe.rs @@ -0,0 +1,167 @@ +use std::{collections::HashMap, ffi::OsStr, fs::File, path::Path}; + +use serde::{de::DeserializeOwned, Deserialize}; +use walkdir::WalkDir; + +use rebel_common::error::*; +use rebel_resolve::task::{TaskDef, TaskMeta}; + +#[derive(Clone, Debug, Deserialize, Default)] +pub struct RecipeMeta { + pub name: Option<String>, + pub version: Option<String>, +} + +#[derive(Debug, Deserialize)] +struct Recipe { + #[serde(default)] + pub meta: RecipeMeta, + pub tasks: HashMap<String, TaskDef>, +} + +#[derive(Debug, Deserialize)] +struct Subrecipe { + pub tasks: HashMap<String, TaskDef>, +} + +fn read_yaml<T: DeserializeOwned>(path: &Path) -> Result<T> { + let f = File::open(path).context("IO error")?; + + let value: T = serde_yaml::from_reader(f) + .map_err(Error::new) + .context("YAML error")?; + + Ok(value) +} + +const RECIPE_NAME: &str = "build"; +const RECIPE_PREFIX: &str = "build."; + +fn recipe_name(path: &Path) -> Option<&str> { + if path.extension() != Some("yml".as_ref()) { + return None; + } + + let stem = path.file_stem()?.to_str()?; + if stem == RECIPE_NAME { + return Some(""); + } + stem.strip_prefix(RECIPE_PREFIX) +} + +fn handle_recipe_tasks( + tasks: &mut HashMap<String, HashMap<String, Vec<TaskDef>>>, + recipe_tasks: HashMap<String, TaskDef>, + meta: &TaskMeta, +) { + let task_map = match tasks.get_mut(&meta.recipe) { + Some(task_map) => task_map, + None => tasks.entry(meta.recipe.clone()).or_default(), + }; + + for (label, mut task) in recipe_tasks { + task.meta = meta.clone(); + task_map.entry(label).or_default().push(task); + } +} + +fn read_recipe_tasks( + path: &Path, + basename: &str, + tasks: &mut HashMap<String, HashMap<String, Vec<TaskDef>>>, +) -> Result<RecipeMeta> { + let recipe_def = read_yaml::<Recipe>(path)?; + + let name = recipe_def + .meta + .name + .as_deref() + .unwrap_or(basename) + .to_string(); + + let meta = TaskMeta { + basename: basename.to_string(), + recipename: "".to_string(), + recipe: basename.to_string(), + name, + version: recipe_def.meta.version.clone(), + }; + + handle_recipe_tasks(tasks, recipe_def.tasks, &meta); + + Ok(recipe_def.meta) +} + +fn read_subrecipe_tasks( + path: &Path, + basename: &str, + recipename: &str, + recipe_meta: &RecipeMeta, + tasks: &mut HashMap<String, HashMap<String, Vec<TaskDef>>>, +) -> Result<()> { + let recipe = format!("{basename}/{recipename}"); + let recipe_def = read_yaml::<Subrecipe>(path)?; + + let name = recipe_meta.name.as_deref().unwrap_or(basename).to_string(); + + let meta = TaskMeta { + basename: basename.to_string(), + recipename: recipename.to_string(), + recipe: recipe.clone(), + name, + version: recipe_meta.version.clone(), + }; + + handle_recipe_tasks(tasks, recipe_def.tasks, &meta); + + Ok(()) +} + +pub fn read_recipes<P: AsRef<Path>>( + path: P, +) -> Result<HashMap<String, HashMap<String, Vec<TaskDef>>>> { + let mut tasks = HashMap::<String, HashMap<String, Vec<TaskDef>>>::new(); + let mut recipe_metas = HashMap::<String, RecipeMeta>::new(); + + for entry in WalkDir::new(path) + .sort_by(|a, b| { + // Files are sorted first by stem, then by extension, so that + // recipe.yml will always be read before recipe.NAME.yml + let stem_cmp = a.path().file_stem().cmp(&b.path().file_stem()); + let ext_cmp = a.path().extension().cmp(&b.path().extension()); + stem_cmp.then(ext_cmp) + }) + .into_iter() + .filter_map(|e| e.ok()) + { + let path = entry.path(); + if !path.is_file() { + continue; + } + + let Some(recipename) = recipe_name(path) else { + continue; + }; + let Some(basename) = path + .parent() + .and_then(Path::file_name) + .and_then(OsStr::to_str) + else { + continue; + }; + + if recipename.is_empty() { + recipe_metas.insert( + basename.to_string(), + read_recipe_tasks(path, basename, &mut tasks)?, + ); + } else { + let Some(recipe_meta) = recipe_metas.get(basename) else { + continue; + }; + read_subrecipe_tasks(path, basename, recipename, recipe_meta, &mut tasks)?; + } + } + + Ok(tasks) +} diff --git a/crates/driver/src/template.rs b/crates/rebel/src/template.rs index 7bb089c..50fb334 100644 --- a/crates/driver/src/template.rs +++ b/crates/rebel/src/template.rs @@ -1,42 +1,39 @@ use handlebars::Handlebars; use lazy_static::lazy_static; -use common::error::*; +use rebel_common::error::*; +use rebel_resolve::args::TaskArgs; -use crate::args::TaskArgs; - -fn escape(s: &str) -> String { +fn escape_sh(s: &str) -> String { format!("'{}'", s.replace('\'', "'\\''")) } #[derive(Debug)] pub struct TemplateEngine { tpl: Handlebars<'static>, - tpl_raw: Handlebars<'static>, + tpl_sh: Handlebars<'static>, } impl TemplateEngine { pub fn new() -> Self { let mut tpl = Handlebars::new(); tpl.set_strict_mode(true); - tpl.register_escape_fn(escape); - - let mut tpl_raw = Handlebars::new(); - tpl_raw.set_strict_mode(true); - tpl_raw.register_escape_fn(handlebars::no_escape); + tpl.register_escape_fn(handlebars::no_escape); - TemplateEngine { tpl, tpl_raw } - } + let mut tpl_sh = Handlebars::new(); + tpl_sh.set_strict_mode(true); + tpl_sh.register_escape_fn(escape_sh); - pub fn eval_raw(&self, input: &str, args: &TaskArgs) -> Result<String> { - self.tpl_raw - .render_template(input, args) - .map_err(Error::new) + TemplateEngine { tpl, tpl_sh } } pub fn eval(&self, input: &str, args: &TaskArgs) -> Result<String> { self.tpl.render_template(input, args).map_err(Error::new) } + + pub fn eval_sh(&self, input: &str, args: &TaskArgs) -> Result<String> { + self.tpl_sh.render_template(input, args).map_err(Error::new) + } } lazy_static! { diff --git a/examples/pins.yml.example b/examples/pins.yml.example index f4dc98f..c16cedb 100644 --- a/examples/pins.yml.example +++ b/examples/pins.yml.example @@ -2,12 +2,12 @@ rootfs-8d1fb3274ebc: hash: '8d1fb3274ebcb0f7ae88f3ce5810ad20014d08efca29e617a873670949de729d' is-rootfs: true provides: - - recipe: 'gcc-libs' + - recipe: 'gcc/libs' task: 'install' output: ['default'] args: host: 'build' - - recipe: 'linux-uapi-headers' + - recipe: 'linux/uapi-headers' task: 'install' output: ['default'] args: @@ -54,7 +54,7 @@ rootfs-8d1fb3274ebc: args: host: 'build' target: 'build' - - recipe: 'libgcc' + - recipe: 'gcc/libgcc' task: 'install' output: ['default'] args: diff --git a/examples/recipes/bar.yml b/examples/recipes/bar/build.yml index f25322e..f25322e 100644 --- a/examples/recipes/bar.yml +++ b/examples/recipes/bar/build.yml diff --git a/examples/recipes/binutils@2.41.yml b/examples/recipes/binutils/build.yml index d6271a9..dd82d2e 100644 --- a/examples/recipes/binutils@2.41.yml +++ b/examples/recipes/binutils/build.yml @@ -1,16 +1,18 @@ +meta: + version: '2.41' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' + - name: '{{name}}-{{version}}.tar.xz' sha256: 'ae9a5789e23459e59606e6714723f2d3ffc31c03174191ef0d015bdf06007450' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz configure: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'toolchain' @@ -19,9 +21,9 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --build={{build.gnu_triplet}} \ --host={{host.gnu_triplet}} \ --target={{target.gnu_triplet}} \ @@ -42,10 +44,10 @@ tasks: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make find -name config.log -delete @@ -53,7 +55,7 @@ tasks: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -61,5 +63,5 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install diff --git a/examples/recipes/busybox@1.36.1.yml b/examples/recipes/busybox/build.yml index abed443..fa87bc3 100644 --- a/examples/recipes/busybox@1.36.1.yml +++ b/examples/recipes/busybox/build.yml @@ -1,27 +1,29 @@ +meta: + version: '1.36.1' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.bz2' + - name: '{{name}}-{{version}}.tar.bz2' sha256: 'b8cc24c9574d809e7279c3be349795c5d5ceb6fdf19ca709f80cde50e47de314' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.bz2 + tar xf {{dldir}}/{{name}}-{{version}}.tar.bz2 configure: - inherit: + parent: task: 'unpack' run: | - mkdir {{pn}}-build - cd {{pn}}-build + mkdir {{name}}-build + cd {{name}}-build make \ - -f ../{{pn}}-{{pv}}/Makefile \ - KBUILD_SRC=../{{pn}}-{{pv}} \ + -f ../{{name}}-{{version}}/Makefile \ + KBUILD_SRC=../{{name}}-{{version}} \ defconfig compile: args: host: 'platform' - inherit: + parent: task: 'configure' build_depends: - recipe: 'toolchain' @@ -30,21 +32,21 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - cd {{pn}}-build + cd {{name}}-build make \ ARCH={{host.karch}} \ CROSS_COMPILE={{cross_compile}} \ EXTRA_CFLAGS='-DBB_EXTRA_VERSION=""' \ - {{pn}} busybox.links + {{name}} busybox.links sed -i \ -e 's@^/usr@@' \ -e 's@^/sbin@/bin@' \ - {{pn}}.links + {{name}}.links install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -52,7 +54,7 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - cd {{pn}}-build + cd {{name}}-build make \ ARCH={{host.karch}} \ CROSS_COMPILE={{cross_compile}} \ diff --git a/examples/recipes/e2fsprogs@1.47.0.yml b/examples/recipes/e2fsprogs/build.yml index 9925772..508d1d4 100644 --- a/examples/recipes/e2fsprogs@1.47.0.yml +++ b/examples/recipes/e2fsprogs/build.yml @@ -1,15 +1,17 @@ +meta: + version: '1.47.0' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' + - name: '{{name}}-{{version}}.tar.xz' sha256: '144af53f2bbd921cef6f8bea88bb9faddca865da3fbc657cc9b4d2001097d5db' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz configure: args: host: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'toolchain' @@ -18,9 +20,9 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --build={{build.gnu_triplet}} \ --host={{host.gnu_triplet}} \ --prefix={{host.prefix}} @@ -29,17 +31,17 @@ tasks: compile: args: host: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make find -name config.log -delete install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -47,6 +49,6 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install diff --git a/examples/recipes/foo.yml b/examples/recipes/foo/build.yml index 2982795..2982795 100644 --- a/examples/recipes/foo.yml +++ b/examples/recipes/foo/build.yml diff --git a/examples/recipes/libgcc-initial@13.2.0.yml b/examples/recipes/gcc/build.libgcc-initial.yml index 43f6f45..ed230ba 100644 --- a/examples/recipes/libgcc-initial@13.2.0.yml +++ b/examples/recipes/gcc/build.libgcc-initial.yml @@ -1,18 +1,16 @@ -meta: - name: 'gcc' tasks: configure: args: host: 'platform' target: 'platform' - inherit: + parent: recipe: 'gcc' task: 'compile' depends: - recipe: 'gcc' task: 'header-stubs' run: | - cd {{pn}}-build + cd {{name}}-build make configure-target-libgcc # A hack borrowed from OpenEmbedded: @@ -30,22 +28,22 @@ tasks: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make all-target-libgcc install: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'compile' output: default: {} run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install-target-libgcc - ln -s libgcc.a {{destdir}}{{host.prefix}}/lib/gcc/{{target.gnu_triplet}}/"$(cat ../{{pn}}-{{pv}}/gcc/BASE-VER)"/libgcc_eh.a + ln -s libgcc.a {{destdir}}{{host.prefix}}/lib/gcc/{{target.gnu_triplet}}/"$(cat ../{{name}}-{{version}}/gcc/BASE-VER)"/libgcc_eh.a diff --git a/examples/recipes/libgcc@13.2.0.yml b/examples/recipes/gcc/build.libgcc.yml index 96a2ce8..67e21f7 100644 --- a/examples/recipes/libgcc@13.2.0.yml +++ b/examples/recipes/gcc/build.libgcc.yml @@ -1,11 +1,9 @@ -meta: - name: 'gcc' tasks: compile: args: host: 'platform' target: 'platform' - inherit: + parent: recipe: 'gcc' task: 'compile' depends: @@ -14,19 +12,19 @@ tasks: args: host: 'target' run: | - cd {{pn}}-build + cd {{name}}-build make all-target-libgcc install: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'compile' output: default: {} run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install-target-libgcc rm {{destdir}}{{host_to_target.sysroot}}{{target.prefix}}/lib/libgcc_s.so* rmdir -p --ignore-fail-on-non-empty {{destdir}}{{host_to_target.sysroot}}{{target.prefix}}/lib diff --git a/examples/recipes/gcc-libs@13.2.0.yml b/examples/recipes/gcc/build.libs.yml index 5320d29..856fd1c 100644 --- a/examples/recipes/gcc-libs@13.2.0.yml +++ b/examples/recipes/gcc/build.libs.yml @@ -1,29 +1,27 @@ -meta: - name: 'gcc' tasks: compile: args: host: 'platform' - inherit: - recipe: 'libgcc' + parent: + recipe: 'gcc/libgcc' task: 'compile' args: host: 'build' target: 'host' run: | - cd {{pn}}-build + cd {{name}}-build make all-target-libatomic all-target-libgomp all-target-libquadmath all-target-libstdc++-v3 install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: path: 'sysroot' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}}/install install-target-libgcc install-target-libatomic install-target-libgomp install-target-libquadmath install-target-libstdc++-v3 rm -r {{destdir}}/install{{build.prefix}}/lib/gcc mv -T {{destdir}}/install/{{build_to_host.sysroot}} {{destdir}}/sysroot diff --git a/examples/recipes/gcc@13.2.0.yml b/examples/recipes/gcc/build.yml index f59fd41..3352b2d 100644 --- a/examples/recipes/gcc@13.2.0.yml +++ b/examples/recipes/gcc/build.yml @@ -1,12 +1,14 @@ +meta: + version: '13.2.0' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' + - name: '{{name}}-{{version}}.tar.xz' sha256: 'e275e76442a6067341a27f04c5c6b83d8613144004c0413528863dc6b5c743da' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz - sed -i -e 's@^MULTILIB_OSDIRNAMES@# &@' {{pn}}-{{pv}}/gcc/config/*/t-* + sed -i -e 's@^MULTILIB_OSDIRNAMES@# &@' {{name}}-{{version}}/gcc/config/*/t-* header-stubs: args: @@ -24,7 +26,7 @@ tasks: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'toolchain' @@ -45,9 +47,9 @@ tasks: run: | export CXX_FOR_TARGET="$CXX_FOR_TARGET -nostdinc++" - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --build={{build.gnu_triplet}} \ --host={{host.gnu_triplet}} \ --target={{target.gnu_triplet}} \ @@ -58,7 +60,7 @@ tasks: --with-build-sysroot={{sysroot}} \ --with-native-system-header-dir={{target.prefix}}/include \ --with-toolexeclibdir={{host_to_target.sysroot}}{{target.prefix}}/lib \ - --with-gxx-include-dir={{host_to_target.sysroot}}{{target.prefix}}/include/c++/"$(cat ../{{pn}}-{{pv}}/gcc/BASE-VER)" \ + --with-gxx-include-dir={{host_to_target.sysroot}}{{target.prefix}}/include/c++/"$(cat ../{{name}}-{{version}}/gcc/BASE-VER)" \ --with-gnu-ld \ --enable-shared \ --enable-languages=c,c++ \ @@ -93,13 +95,13 @@ tasks: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'configure' depends: - task: 'header-stubs' noinherit: true run: | - cd {{pn}}-build + cd {{name}}-build make all-host find -name config.log -delete @@ -107,7 +109,7 @@ tasks: args: host: 'platform' target: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -119,6 +121,6 @@ tasks: - recipe: 'zlib' task: 'install' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install-host diff --git a/examples/recipes/glibc@2.38.yml b/examples/recipes/glibc/build.yml index 12fb7a4..f1bf890 100644 --- a/examples/recipes/glibc@2.38.yml +++ b/examples/recipes/glibc/build.yml @@ -1,30 +1,32 @@ +meta: + version: '2.38-25-gf6445dc94da1' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' - sha256: 'fb82998998b2b29965467bc1b69d152e9c307d2cf301c9eafb4555b770ef3fd2' + - name: '{{name}}-{{version}}.tar.xz' + sha256: '92594ade540761f70d57de4f3a150128e7b7f7ed653a59d1acc23468ab70fae9' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz configure: args: host: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'gcc' task: 'install' - - recipe: 'libgcc-initial' + - recipe: 'gcc/libgcc-initial' task: 'install' depends: - - recipe: 'linux-uapi-headers' + - recipe: 'linux/uapi-headers' task: 'install' run: | export BUILD_CC="$CC_FOR_BUILD" - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --build={{build.gnu_triplet}} \ --host={{host.gnu_triplet}} \ --prefix={{host.prefix}} \ @@ -45,22 +47,22 @@ tasks: compile: args: host: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: runtime_depends: - - recipe: 'linux-uapi-headers' + - recipe: 'linux/uapi-headers' task: 'install' run: | - cd {{pn}}-build + cd {{name}}-build make install_root={{destdir}} install diff --git a/examples/recipes/gmp/build.recipe b/examples/recipes/gmp/build.recipe new file mode 100644 index 0000000..44e87d6 --- /dev/null +++ b/examples/recipes/gmp/build.recipe @@ -0,0 +1,69 @@ +// External definitions used by this recipe: +// +// workdir: String +// name: String +// destdir: String +// build: Platform +// build_depend: (task: TaskID) -> TaskDep +// host_depend: (task: TaskID) -> TaskDep + +version = "6.3.0"; +sourcedir = "{{workdir}}/{{name}}-{{version}}"; +builddir = "{{workdir}}/{{name}}-build"; + +fetch source { + url = ["https://invalid/{{name}}-{{version}}.tar.xz"]; + // TODO: Move to lockfile + sha256 = "a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898"; +} + +task unpack() { + depends = [source]; + + run = ``` + tar xf {{source.path}} + ```; +} + +task configure(host: Platform) { + parent = unpack(); + depends = [ + build_depend(toolchain::build_depends), + host_depend(toolchain::depends), + ]; + + run = ``` + mkdir {{builddir}} + cd {{builddir}} + {{sourcedir}}/configure \ + --build={{build.gnu_triplet}} \ + --host={{host.gnu_triplet}} \ + --prefix={{host.prefix}} + ```; +} + +task compile(host: Platform) { + parent = configure(host); + + run = ``` + cd {{builddir}} + make + ```; +} + +task install(host: Platform) { + parent = compile(host); + + output = { + default = { + runtime_depends = [host_depend(toolchain::depends)], + }, + }; + + run = ``` + cd {{builddir}} + make DESTDIR={{destdir}} install + rm {{destdir}}{{host.prefix}}/lib/*.a + rm {{destdir}}{{host.prefix}}/lib/*.la + ```; +} diff --git a/examples/recipes/gmp@6.3.0.yml b/examples/recipes/gmp/build.yml index 8ff3385..322f0f0 100644 --- a/examples/recipes/gmp@6.3.0.yml +++ b/examples/recipes/gmp/build.yml @@ -1,15 +1,17 @@ +meta: + version: '6.3.0' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' + - name: '{{name}}-{{version}}.tar.xz' sha256: 'a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz configure: args: host: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'toolchain' @@ -18,9 +20,9 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --build={{build.gnu_triplet}} \ --host={{host.gnu_triplet}} \ --prefix={{host.prefix}} @@ -29,16 +31,16 @@ tasks: compile: args: host: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -46,7 +48,7 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install rm {{destdir}}{{host.prefix}}/lib/*.a rm {{destdir}}{{host.prefix}}/lib/*.la diff --git a/examples/recipes/image.yml b/examples/recipes/image/build.yml index 883fa3b..a42899f 100644 --- a/examples/recipes/image.yml +++ b/examples/recipes/image/build.yml @@ -15,12 +15,13 @@ tasks: mkdir -p \ {{destdir}}/dev \ {{destdir}}/proc \ - {{destdir}}/sys \ {{destdir}}/root \ {{destdir}}/run \ - {{destdir}}/var \ + {{destdir}}/sys \ + {{destdir}}/tmp \ {{destdir}}/usr/bin \ - {{destdir}}/usr/lib + {{destdir}}/usr/lib \ + {{destdir}}/var ln -s bin {{destdir}}/usr/sbin ln -s usr/bin {{destdir}}/bin @@ -44,13 +45,16 @@ tasks: output: default: {} run: | - IMAGE={{destdir}}/{{pn}}.ext4 - UUID="$( - python3 \ - -c 'import sys; import uuid; print(uuid.UUID(hex=sys.argv[1][:32], version=4))' \ - "$INPUT_HASH" - )" + IMAGE={{destdir}}/{{name}}.ext4 mkdir {{destdir}} make_ext4fs -l 2G -T "$SOURCE_DATE_EPOCH" -L root "$IMAGE" {{sysroot}} + E2FSPROGS_FAKE_TIME="$SOURCE_DATE_EPOCH" tune2fs -U '00000000-0000-4000-8000-000000000000' "$IMAGE" + + HASH=$(sha256sum "$IMAGE") + UUID=$( + python3 \ + -c 'import sys; import uuid; print(uuid.UUID(hex=sys.argv[1][:32], version=4))' \ + "$HASH" + ) E2FSPROGS_FAKE_TIME="$SOURCE_DATE_EPOCH" tune2fs -U "$UUID" "$IMAGE" diff --git a/examples/recipes/linux-uapi-headers@6.5.5.yml b/examples/recipes/linux/build.uapi-headers.yml index c431105..a302aa9 100644 --- a/examples/recipes/linux-uapi-headers@6.5.5.yml +++ b/examples/recipes/linux/build.uapi-headers.yml @@ -1,21 +1,19 @@ -meta: - name: 'linux' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' + - name: '{{name}}-{{version}}.tar.xz' sha256: '8cf10379f7df8ea731e09bff3d0827414e4b643dd41dc99d0af339669646ef95' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz install: args: host: 'platform' - inherit: + parent: task: 'unpack' output: default: {} run: | - cd {{pn}}-{{pv}} + cd {{name}}-{{version}} make INSTALL_HDR_PATH={{destdir}}{{host.prefix}} ARCH={{host.karch}} headers_install diff --git a/examples/recipes/linux@6.5.5.yml b/examples/recipes/linux/build.yml index 77f5879..3f84291 100644 --- a/examples/recipes/linux@6.5.5.yml +++ b/examples/recipes/linux/build.yml @@ -1,24 +1,26 @@ +meta: + version: '6.5.5' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' + - name: '{{name}}-{{version}}.tar.xz' sha256: '8cf10379f7df8ea731e09bff3d0827414e4b643dd41dc99d0af339669646ef95' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz configure: args: host: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'gcc' task: 'install' run: | - mkdir {{pn}}-build - cd {{pn}}-build + mkdir {{name}}-build + cd {{name}}-build make \ - -f ../{{pn}}-{{pv}}/Makefile \ + -f ../{{name}}-{{version}}/Makefile \ ARCH={{host.karch}} \ CROSS_COMPILE={{cross_compile}} \ defconfig @@ -26,12 +28,12 @@ tasks: compile: args: host: 'platform' - inherit: + parent: task: 'configure' run: | export KBUILD_BUILD_TIMESTAMP="@${SOURCE_DATE_EPOCH}" - cd {{pn}}-build + cd {{name}}-build make \ ARCH={{host.karch}} \ CROSS_COMPILE={{cross_compile}} \ @@ -40,7 +42,7 @@ tasks: install: args: host: 'platform' - inherit: + parent: task: 'compile' output: boot: @@ -48,7 +50,7 @@ tasks: modules: path: 'modules' run: | - cd {{pn}}-build + cd {{name}}-build mkdir -p {{destdir}}/boot make \ ARCH={{host.karch}} \ diff --git a/examples/recipes/ls.yml b/examples/recipes/ls/build.yml index 633336d..633336d 100644 --- a/examples/recipes/ls.yml +++ b/examples/recipes/ls/build.yml diff --git a/examples/recipes/make_ext4fs@2020-01-05.yml b/examples/recipes/make_ext4fs/build.yml index 53fed98..183389a 100644 --- a/examples/recipes/make_ext4fs@2020-01-05.yml +++ b/examples/recipes/make_ext4fs/build.yml @@ -3,10 +3,10 @@ meta: tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.gz' + - name: '{{name}}-{{version}}.tar.gz' sha256: 'bfe984cc757c676639090a5b34bdfc359cdef9b723de77efbce590872dce4132' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.gz + tar xf {{dldir}}/{{name}}-{{version}}.tar.gz compile: args: @@ -19,16 +19,16 @@ tasks: task: 'depends' - recipe: 'zlib' task: 'install' - inherit: + parent: task: 'unpack' run: | - cd {{pn}}-{{pv}} + cd {{name}}-{{version}} make install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -38,7 +38,7 @@ tasks: - recipe: 'zlib' task: 'install' run: | - cd {{pn}}-{{pv}} + cd {{name}}-{{version}} install -d {{destdir}}{{host.prefix}}/bin install -m755 make_ext4fs {{destdir}}{{host.prefix}}/bin/ diff --git a/examples/recipes/mpc@1.3.1.yml b/examples/recipes/mpc/build.yml index 835b0af..e415c60 100644 --- a/examples/recipes/mpc@1.3.1.yml +++ b/examples/recipes/mpc/build.yml @@ -1,15 +1,17 @@ +meta: + version: '1.3.1' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.gz' + - name: '{{name}}-{{version}}.tar.gz' sha256: 'ab642492f5cf882b74aa0cb730cd410a81edcdbec895183ce930e706c1c759b8' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.gz + tar xf {{dldir}}/{{name}}-{{version}}.tar.gz configure: args: host: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'toolchain' @@ -20,9 +22,9 @@ tasks: - recipe: 'mpfr' task: 'install' run: | - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --build={{build.gnu_triplet}} \ --host={{host.gnu_triplet}} \ --prefix={{host.prefix}} @@ -31,16 +33,16 @@ tasks: compile: args: host: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -50,7 +52,7 @@ tasks: - recipe: 'mpfr' task: 'install' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install rm {{destdir}}{{host.prefix}}/lib/*.a rm {{destdir}}{{host.prefix}}/lib/*.la diff --git a/examples/recipes/mpfr@4.2.1.yml b/examples/recipes/mpfr/build.yml index 5747932..619bec7 100644 --- a/examples/recipes/mpfr@4.2.1.yml +++ b/examples/recipes/mpfr/build.yml @@ -1,15 +1,17 @@ +meta: + version: '4.2.1' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.xz' + - name: '{{name}}-{{version}}.tar.xz' sha256: '277807353a6726978996945af13e52829e3abd7a9a5b7fb2793894e18f1fcbb2' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.xz + tar xf {{dldir}}/{{name}}-{{version}}.tar.xz configure: args: host: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'toolchain' @@ -20,9 +22,9 @@ tasks: - recipe: 'gmp' task: 'install' run: | - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --build={{build.gnu_triplet}} \ --host={{host.gnu_triplet}} \ --prefix={{host.prefix}} @@ -31,16 +33,16 @@ tasks: compile: args: host: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -50,7 +52,7 @@ tasks: - recipe: 'gmp' task: 'install' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install rm {{destdir}}{{host.prefix}}/lib/*.a rm {{destdir}}{{host.prefix}}/lib/*.la diff --git a/examples/recipes/toolchain.yml b/examples/recipes/toolchain/build.yml index ab8e6de..d612261 100644 --- a/examples/recipes/toolchain.yml +++ b/examples/recipes/toolchain/build.yml @@ -8,7 +8,7 @@ tasks: runtime_depends: - recipe: 'gcc' task: 'install' - - recipe: 'libgcc' + - recipe: 'gcc/libgcc' task: 'install' depends: @@ -19,7 +19,7 @@ tasks: runtime_depends: - recipe: 'glibc' task: 'install' - - recipe: 'gcc-libs' + - recipe: 'gcc/libs' task: 'install' build: diff --git a/examples/recipes/zlib@1.3.yml b/examples/recipes/zlib/build.yml index 0102e74..3e72844 100644 --- a/examples/recipes/zlib@1.3.yml +++ b/examples/recipes/zlib/build.yml @@ -1,15 +1,17 @@ +meta: + version: '1.3' tasks: unpack: fetch: - - name: '{{pn}}-{{pv}}.tar.gz' + - name: '{{name}}-{{version}}.tar.gz' sha256: 'ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e' run: | - tar xf {{dldir}}/{{pn}}-{{pv}}.tar.gz + tar xf {{dldir}}/{{name}}-{{version}}.tar.gz configure: args: host: 'platform' - inherit: + parent: task: 'unpack' build_depends: - recipe: 'toolchain' @@ -18,25 +20,25 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - mkdir {{pn}}-build - cd {{pn}}-build - ../{{pn}}-{{pv}}/configure \ + mkdir {{name}}-build + cd {{name}}-build + ../{{name}}-{{version}}/configure \ --prefix={{host.prefix}} find -name configure.log -delete compile: args: host: 'platform' - inherit: + parent: task: 'configure' run: | - cd {{pn}}-build + cd {{name}}-build make install: args: host: 'platform' - inherit: + parent: task: 'compile' output: default: @@ -44,7 +46,7 @@ tasks: - recipe: 'toolchain' task: 'depends' run: | - cd {{pn}}-build + cd {{name}}-build make DESTDIR={{destdir}} install rm {{destdir}}{{host.prefix}}/lib/*.a |