├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── TO_DO ├── _bin ├── find_cc_ld_library_path ├── gen_gcov.py ├── lrun_cc ├── sys_config ├── ut_launch └── version ├── _lib ├── fmt_rule.src.py ├── lmake │ ├── __init__.src.py │ ├── auto_sources.py │ ├── config.src.py │ ├── import_machinery.py │ ├── py_clmake.src.py │ ├── rules.src.py │ ├── sources.src.py │ └── utils.py ├── lmake_debug │ ├── __init__.py │ ├── default.py │ ├── enter.py │ ├── gdb.py │ ├── none.py │ ├── pudb.py │ ├── runtime │ │ ├── __init__.py │ │ ├── pdb_.py │ │ ├── pudb_.py │ │ ├── utils.py │ │ └── vscode.py │ ├── utils.py │ └── vscode.py ├── read_makefiles.src.py ├── serialize.py └── ut.py ├── apparmor-profile ├── debian ├── changelog.src ├── control ├── copyright ├── open-lmake.lintian-overrides ├── open-lmake.postinst ├── open-lmake.prerm ├── rules └── source │ └── format ├── doc ├── benchmark.md ├── book.toml ├── developers.md ├── fearless.md ├── lmake_doc.pptx ├── man │ ├── man1 │ │ ├── common.1.m │ │ ├── lautodep.1.m │ │ ├── lcheck_deps.1.m │ │ ├── ldebug.1.m │ │ ├── ldecode.1.m │ │ ├── ldepend.1.m │ │ ├── lencode.1.m │ │ ├── lforget.1.m │ │ ├── lmake.1.m │ │ ├── lmark.1.m │ │ ├── lrepair.1.m │ │ ├── lrun_cc.1.m │ │ ├── lshow.1.m │ │ ├── ltarget.1.m │ │ └── xxhsum.1.m │ └── utils.mh ├── quick_comparison.md ├── src │ ├── SUMMARY.md │ ├── autodep.md │ ├── backends.md │ ├── commands.md │ ├── config.md │ ├── critical_deps.md │ ├── data_model.md │ ├── eta.md │ ├── execution.md │ ├── experimental.md │ ├── experimental_cache.md │ ├── experimental_codec.md │ ├── experimental_subrepos.md │ ├── faq.md │ ├── glossary.md │ ├── install.md │ ├── intro.md │ ├── job_execution.md │ ├── lmake_import_machinery_module.md │ ├── lmake_module.md │ ├── lmake_rules_module.md │ ├── lmake_sources_module.md │ ├── meta_data.md │ ├── namespaces.md │ ├── overview.md │ ├── rule_selection.md │ ├── rules.md │ ├── video_mode.md │ └── writing_lmakefile.md ├── who_needs_open-lmake.md └── why_stick_to_alpha_algo.md ├── docker ├── centos7.docker ├── debian12.docker ├── rocky8.docker ├── rocky9.docker ├── suse156.docker ├── ubuntu20.docker ├── ubuntu22.docker └── ubuntu24.docker ├── docs ├── .nojekyll ├── 404.html ├── FontAwesome │ ├── css │ │ └── font-awesome.css │ └── fonts │ │ ├── FontAwesome.ttf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── autodep.html ├── ayu-highlight.css ├── backends.html ├── book.js ├── clipboard.min.js ├── commands.html ├── config.html ├── critical_deps.html ├── css │ ├── chrome.css │ ├── general.css │ ├── print.css │ └── variables.css ├── data_model.html ├── elasticlunr.min.js ├── eta.html ├── execution.html ├── experimental.html ├── experimental_cache.html ├── experimental_codec.html ├── experimental_subrepos.html ├── faq.html ├── favicon.png ├── favicon.svg ├── fonts │ ├── OPEN-SANS-LICENSE.txt │ ├── SOURCE-CODE-PRO-LICENSE.txt │ ├── fonts.css │ ├── open-sans-v17-all-charsets-300.woff2 │ ├── open-sans-v17-all-charsets-300italic.woff2 │ ├── open-sans-v17-all-charsets-600.woff2 │ ├── open-sans-v17-all-charsets-600italic.woff2 │ ├── open-sans-v17-all-charsets-700.woff2 │ ├── open-sans-v17-all-charsets-700italic.woff2 │ ├── open-sans-v17-all-charsets-800.woff2 │ ├── open-sans-v17-all-charsets-800italic.woff2 │ ├── open-sans-v17-all-charsets-italic.woff2 │ ├── open-sans-v17-all-charsets-regular.woff2 │ └── source-code-pro-v11-all-charsets-500.woff2 ├── glossary.html ├── highlight.css ├── highlight.js ├── index.html ├── install.html ├── intro.html ├── job_execution.html ├── lmake_import_machinery_module.html ├── lmake_module.html ├── lmake_rules_module.html ├── lmake_sources_module.html ├── man │ └── man1 │ │ ├── lautodep.html │ │ ├── lcheck_deps.html │ │ ├── ldebug.html │ │ ├── ldecode.html │ │ ├── ldepend.html │ │ ├── lencode.html │ │ ├── lforget.html │ │ ├── lmake.html │ │ ├── lmark.html │ │ ├── lrepair.html │ │ ├── lrun_cc.html │ │ ├── lshow.html │ │ ├── ltarget.html │ │ └── xxhsum.html ├── mark.min.js ├── meta_data.html ├── namespaces.html ├── overview.html ├── print.html ├── rule_selection.html ├── rules.html ├── searcher.js ├── searchindex.js ├── searchindex.json ├── toc.html ├── toc.js ├── tomorrow-night.css ├── video_mode.html └── writing_lmakefile.html ├── examples ├── cc.dir │ ├── Lmakefile.py │ ├── Manifest │ ├── hello.c │ ├── hello.h │ ├── hello_world.c │ ├── hello_world.regr │ ├── run │ ├── world.c │ └── world.h ├── cpp20_modules.dir │ ├── Lmakefile.py │ ├── Manifest │ ├── another.mpp │ ├── duplicate.mpp │ ├── run │ └── use.mpp └── hello_world.dir │ ├── Lmakefile.py │ ├── hello │ ├── run │ └── world ├── ext ├── slurm │ ├── 21.08 │ │ └── slurm │ │ │ ├── slurm.h │ │ │ ├── slurm_errno.h │ │ │ └── slurm_version.h │ ├── 22.05 │ │ └── slurm │ │ │ ├── slurm.h │ │ │ ├── slurm_errno.h │ │ │ └── slurm_version.h │ ├── 23.02 │ │ └── slurm │ │ │ ├── slurm.h │ │ │ ├── slurm_errno.h │ │ │ └── slurm_version.h │ ├── 23.11 │ │ └── slurm │ │ │ ├── slurm.h │ │ │ ├── slurm_errno.h │ │ │ └── slurm_version.h │ ├── 24.05 │ │ └── slurm │ │ │ ├── slurm.h │ │ │ ├── slurm_errno.h │ │ │ └── slurm_version.h │ └── 24.11 │ │ └── slurm │ │ ├── slurm.h │ │ ├── slurm_errno.h │ │ └── slurm_version.h └── xxhash.h ├── lmake_env ├── Lmakefile.py └── ext_lnk ├── museum ├── _lib │ └── lmake │ │ └── custom_importer.py ├── darwin_diff ├── restore_fuse_diff ├── src │ ├── autodep │ │ ├── fuse.cc │ │ └── fuse.hh │ ├── fuse.cc │ ├── fuse.hh │ ├── fuse_test.cc │ ├── store │ │ ├── red_black.hh │ │ └── restore_red_black_diff │ └── stream.hh └── unit_tests │ └── fuse.py ├── src ├── align_comments.cc ├── app.cc ├── app.hh ├── autodep │ ├── backdoor.cc │ ├── backdoor.hh │ ├── clmake.cc │ ├── elf.hh │ ├── env.cc │ ├── env.hh │ ├── gather.cc │ ├── gather.hh │ ├── job_support.cc │ ├── job_support.hh │ ├── lautodep.cc │ ├── lcheck_deps.cc │ ├── ld_audit.cc │ ├── ld_common.x.cc │ ├── ld_preload.cc │ ├── ld_preload_jemalloc.cc │ ├── ld_server.cc │ ├── ld_server.hh │ ├── ldecode.cc │ ├── ldepend.cc │ ├── lencode.cc │ ├── ltarget.cc │ ├── ptrace.cc │ ├── ptrace.hh │ ├── record.cc │ ├── record.hh │ ├── syscall_tab.cc │ └── syscall_tab.hh ├── basic_utils.hh ├── caches │ ├── dir_cache.cc │ └── dir_cache.hh ├── client.cc ├── client.hh ├── disk.cc ├── disk.hh ├── enum.hh ├── fd.cc ├── fd.hh ├── hash.cc ├── hash.hh ├── job_exec.cc ├── ldebug.cc ├── ldump.cc ├── ldump_job.cc ├── lforget.cc ├── lib.cc ├── lib.hh ├── lkpi.cc ├── lmake.cc ├── lmakeserver │ ├── backend.cc │ ├── backend.x.hh │ ├── backends │ │ ├── generic.hh │ │ ├── local.cc │ │ ├── sge.cc │ │ ├── slurm.cc │ │ ├── slurm_api.hh │ │ └── slurm_api.x.cc │ ├── cmd.cc │ ├── cmd.hh │ ├── codec.cc │ ├── codec.hh │ ├── config.cc │ ├── config.x.hh │ ├── core.hh │ ├── core.x.hh │ ├── global.cc │ ├── global.x.hh │ ├── idxed.hh │ ├── job.cc │ ├── job.x.hh │ ├── main.cc │ ├── makefiles.cc │ ├── makefiles.hh │ ├── node.cc │ ├── node.x.hh │ ├── req.cc │ ├── req.x.hh │ ├── rule.cc │ ├── rule.x.hh │ ├── store.cc │ └── store.x.hh ├── lmark.cc ├── lrepair.cc ├── lshow.cc ├── msg.hh ├── non_portable.cc ├── non_portable.hh ├── process.cc ├── process.hh ├── py.cc ├── py.hh ├── re.cc ├── re.hh ├── rpc_client.cc ├── rpc_client.hh ├── rpc_job.cc ├── rpc_job.hh ├── rpc_job_common.hh ├── rpc_job_exec.cc ├── rpc_job_exec.hh ├── serialize.hh ├── std.hh ├── store │ ├── alloc.hh │ ├── big_test.py │ ├── file.hh │ ├── prefix.hh │ ├── side_car.hh │ ├── store_utils.hh │ ├── struct.hh │ ├── unit_test.cc │ └── vector.hh ├── thread.hh ├── time.cc ├── time.hh ├── trace.cc ├── trace.hh ├── types.hh ├── utils.cc ├── utils.hh └── xxhsum.cc └── unit_tests ├── abort.py ├── admin.py ├── back_ref.py ├── base ├── Lmakefile.py ├── hello.py ├── src1 └── src2 ├── basics.py ├── bench.py ├── cache.py ├── calc1.py ├── calc2.py ├── callables.py ├── cargo.py ├── chain.py ├── chdir.py ├── check_deps.py ├── clone.cc ├── clone.py ├── cmd.py ├── codec.py ├── conflict.py ├── conflict2.py ├── critical.py ├── critical2.py ├── cycle.py ├── dangling.py ├── depend.py ├── depend2.py ├── depend_verbose.py ├── depend_verbose2.py ├── dup.py ├── dyn.py ├── dyn_accesses.py ├── dyn_config.script ├── dyn_imports.py ├── dyn_py.py ├── dyn_resources.py ├── env.py ├── err.py ├── escape.py ├── exe_32bits.py ├── force.py ├── forget.py ├── gcda.py ├── generic_sources.py ├── git.py ├── hard_link.py ├── hard_lnks.py ├── hide.py ├── home.py ├── hot.py ├── ignore.py ├── ignore_err.py ├── import.py ├── import2.py ├── incremental.py ├── infinite.py ├── interpreter.py ├── jemalloc.py ├── jobs.py ├── kill.py ├── ld_library_path.py ├── link.py ├── live_out.py ├── lmark.py ├── lshow.py ├── mandelbrot.py ├── mandelbrot.zip ├── manual_target.py ├── misc1.py ├── misc10.py ├── misc11.py ├── misc12.py ├── misc13.py ├── misc14.py ├── misc15.py ├── misc16.py ├── misc2.py ├── misc3.py ├── misc4.py ├── misc6.py ├── misc7.py ├── misc8.py ├── misc9.py ├── mk_py1.script ├── mk_py2.script ├── mk_py3.script ├── mk_py4.script ├── mkdir.py ├── modules.py ├── multi.py ├── name.py ├── namespaces.py ├── numba.py ├── numba2.py ├── numpy.py ├── overlay.py ├── path.py ├── path_max.py ├── pdict.py ├── perf.py ├── phony.py ├── ptrace.py ├── py_rule.py ├── py_venv.py ├── pyc.py ├── python2.py ├── qualify.py ├── re.py ├── readdir.py ├── regexpr.py ├── regression.py ├── rename.py ├── repair.py ├── repo.py ├── repo_fake.py ├── rerun.py ├── resources.py ├── retry.py ├── roll_back.py ├── rules.py ├── rust.py ├── scratchpad.py ├── sge.py ├── slurm.py ├── slurm_local.py ├── sources.py ├── sources2.py ├── sources_rules_modules.py ├── speculate.py ├── src.py ├── star.py ├── static_deps.py ├── stress_depend.py ├── sub_file.py ├── sub_repos.py ├── submit_loop.py ├── subprocess.py ├── suspend.py ├── sym_lnk.py ├── tar.py ├── target.py ├── target2.py ├── test1.script ├── test2.script ├── test3.script ├── test4.script ├── test5.script ├── timeout.py ├── tmp.py ├── tmp2.py ├── trace_exec.py ├── transient.py ├── unstable.script ├── uphill.py ├── use_script.py ├── user_env.py ├── valgrind.py ├── vfork.py ├── vfork2.py ├── views.py ├── wide.py ├── wine.py ├── wrong_target.py └── xxh.py /.gitignore: -------------------------------------------------------------------------------- 1 | # vim swap files 2 | .*.sw? 3 | 4 | # python caches 5 | __pycache__ 6 | 7 | # directories 8 | /unit_tests/*.dir 9 | /examples/*.trial 10 | /bin 11 | /lib 12 | 13 | # binaries 14 | /_bin/align_comments 15 | /_bin/job_exec 16 | /_bin/ldump 17 | /_bin/ldump_job 18 | /_bin/lkpi 19 | /_bin/lmakeserver 20 | /_bin/mdbook 21 | 22 | # libs 23 | /_lib/fmt_rule.py 24 | /_lib/read_makefiles.py 25 | 26 | # doc 27 | /man/man1/*.1 28 | /doc/book.actual_manifest 29 | /doc/book.ref_manifest 30 | 31 | #auto-configuration 32 | /sys_config.* 33 | /sys_config_env 34 | /version.hh 35 | /Manifest 36 | *.d 37 | 38 | # compiled files 39 | *.chk 40 | *.chk.err 41 | *.chk.stderr 42 | *.dbg 43 | *.hh.gch 44 | *.i 45 | *.o 46 | *.s 47 | *.so 48 | *.*_stamp 49 | *.stamp 50 | */stamp 51 | src/lmakeserver/backends/slurm_api-*.cc 52 | 53 | # tests 54 | tok 55 | tok.out 56 | tok.err 57 | skipped 58 | *.tok 59 | /src/store/unit_test 60 | /core.* 61 | 62 | # coverage 63 | *.gcda 64 | *.gcno 65 | gcov/ 66 | 67 | # IDE files 68 | /.idea/ 69 | /.vscode/ 70 | 71 | # Lmake env 72 | !/lmake_env/Lmakefile.py 73 | !/lmake_env/ext_lnk 74 | /lmake_env 75 | /lmake_env-cache 76 | 77 | # tarball 78 | /lmake.tar.gz 79 | 80 | # debian 81 | /open-lmake_* 82 | /open-lmake-*/ 83 | -------------------------------------------------------------------------------- /_bin/version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | # Copyright (c) 2023-2025 Doliam 4 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | 7 | version_mrkr=$( 8 | sed -n -e 's://.*\(\(START\|END\)_OF_VERSIONING\).*:\1:' -e 's://.*::' -e 's:/\*.*\*/::g' -e '/START_OF_VERSIONING/,/END_OF_VERSIONING/p' "$@" \ 9 | | grep -vx '\(START\|END\)_OF_VERSIONING' \ 10 | | tr -d ' \t\n' \ 11 | | md5sum \ 12 | | cut -d' ' -f1 13 | ) 14 | cat <, 7 | include # inherit standard profile 8 | 9 | @{INSTALL_DIR}=/usr/lib/open-lmake 10 | profile open-lmake @{INSTALL_DIR}/_bin/job_exec,{@{INSTALL_DIR},/usr,}/bin/lautodep flags=(unconfined) { 11 | userns, 12 | # Site-specific additions and overrides. See local/README for details. 13 | include if exists 14 | } 15 | -------------------------------------------------------------------------------- /debian/changelog.src: -------------------------------------------------------------------------------- 1 | open-lmake ($VERSION_TAG-$DEBIAN_RELEASE) $OS_CODENAME; urgency=low 2 | 3 | * manage readdir syscalls (were ignored in previous versions) 4 | * replace allow_stderr attribute by stderr_ok for homogeneity with readdir_ok 5 | * depend verbose now reports more info on deps 6 | * implement regexpr based lmake.depend/ldepend and lmake.target/ltarget 7 | * improve cache 8 | * add LMAKE/matching file to report candidate rules for each prefix/suffix pair 9 | * add example to show C++20 module support 10 | * support Debian12 and OpenSuse15.6 in addition to CentOS7, Rocky8, Rocky9, Ubuntu20.04, Ubuntu22.04 and Ubuntu24.04 11 | * bug fixes 12 | * improve perf 13 | 14 | -- Cesar Douady $DATE 15 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | Source: open-lmake 7 | Section: devel 8 | Priority: optional 9 | Maintainer: Cesar Douady 10 | Build-Depends: 11 | debhelper-compat (= 13), 12 | dh-apparmor, 13 | ed, 14 | wget, 15 | m4, 16 | groff, 17 | python3, 18 | python3-dev, 19 | strace, 20 | # not strictly necessary but recommended 21 | git, 22 | libseccomp-dev, 23 | libpcre2-dev, 24 | libpcre2-8-0, 25 | zlib1g, 26 | zlib1g-dev, 27 | libzstd-dev 28 | Standards-Version: 4.6.2 29 | Homepage: https://github.com/cesar-douady/open-lmake 30 | 31 | Package: open-lmake 32 | Architecture: amd64 arm64 33 | Depends: 34 | ${misc:Depends}, 35 | ${shlibs:Depends}, 36 | python3, 37 | python3-dev, 38 | # necessary if mentioned at build time, suppress if suppressed at build time 39 | git, 40 | libseccomp-dev, 41 | libpcre2-8-0, 42 | zlib1g, 43 | zlib1g-dev, 44 | libzstd-dev 45 | Recommends: 46 | libslurm37 | libslurm38 | libslurm39 | libslurm40 | libslurm40t64 | libslurm41t64 | libslurm42t64 47 | Description: pythonic workflow management system 48 | Build systems like GNU Make are frequently used to create complicated 49 | workflows. This project aims to reduce the complexity of creating workflows 50 | by automatically handling dependencies, together with a fast and 51 | comfortable execution environment. 52 | It leverages python for the user interface and is scalable to millions of jobs. 53 | -------------------------------------------------------------------------------- /debian/open-lmake.lintian-overrides: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | initial-upload-closes-no-bugs 7 | 8 | # stupid debian does not understand debug symbol files, filter some errors out 9 | binary-with-bad-dynamic-table [usr/lib/open-lmake/**/*.dbg] 10 | shared-library-lacks-prerequisites [usr/lib/open-lmake/**/*.dbg] 11 | -------------------------------------------------------------------------------- /debian/open-lmake.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | # Copyright (c) 2023-2025 Doliam 4 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | 7 | if command -v py3compile >/dev/null 2>&1 ; then 8 | py3compile /usr/lib/open-lmake/_lib/ 9 | py3compile /usr/lib/open-lmake/lib/ 10 | fi 11 | 12 | #DEBHELPER# 13 | -------------------------------------------------------------------------------- /debian/open-lmake.prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | # Copyright (c) 2023-2025 Doliam 4 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | 7 | if command -v py3clean >/dev/null 2>&1 ; then 8 | py3clean /usr/lib/open-lmake/_lib/ 9 | py3clean /usr/lib/open-lmake/lib/ 10 | fi 11 | 12 | if aa-enabled --quiet 2>/dev/null; then 13 | apparmor_parser --remove /etc/apparmor.d/open-lmake || true 14 | fi 15 | 16 | #DEBHELPER# 17 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | # Copyright (c) 2023-2025 Doliam 4 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | 7 | # Uncomment this to turn on verbose mode. 8 | #export DH_VERBOSE = 1 9 | 10 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 11 | 12 | % : 13 | dh $@ 14 | 15 | override_dh_auto_build : 16 | make LMAKE 17 | 18 | override_dh_auto_install : 19 | dh_auto_install 20 | if apparmor_parser --version | grep -q 'version *4\.' ; then dh_apparmor --profile-name=open-lmake -popen-lmake ; fi 21 | 22 | override_dh_strip : 23 | dh_strip --no-automatic-dbgsym 24 | 25 | # stupid debian does not understand debug symbol files, filter them out 26 | override_dh_shlibdeps : 27 | dh_shlibdeps -X.dbg 28 | 29 | override_dh_makeshlibs : 30 | # Do nothing 31 | 32 | override_dh_dwz : 33 | # Do nothing 34 | 35 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /doc/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title="open-lmake documentation" 3 | -------------------------------------------------------------------------------- /doc/lmake_doc.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/doc/lmake_doc.pptx -------------------------------------------------------------------------------- /doc/man/man1/ldecode.1.m: -------------------------------------------------------------------------------- 1 | Comment( 2 | This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | Copyright (c) 2023-2025 Doliam 4 | This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | ) 7 | 8 | Title(ldecode,retrieve the original content encoded with C(lencode) within a OpenLmake job) 9 | .SH SYNOPSYS 10 | B(ldecode) I(association_file) I(context) I(code) 11 | 12 | .SH DESCRIPTION 13 | .LP 14 | B(ldecode) may be used to ask for a value (typically rather large) associated with a short code. 15 | This must have been generated using the command C(lencode) with the same association_file and context. 16 | The value corresponding to I(code) is output on stdout. 17 | .LP 18 | It is an error if 19 | Bullet I(association_file) is not a source (symbolic links are followed, though) 20 | Bullet I(code) cannot be found with the accompanying I(context) 21 | .LP 22 | Usage and use cases are more extensively documented the full OpenLmake documentation. 23 | 24 | .SH NOTES 25 | Item((1)) 26 | The same functionality is provided with the B(lmake.decode) python function. 27 | Item((2)) 28 | C(lencode) and B(ldecode) are useful tools when the flow implies file names whose names are impractical. 29 | This is a way to transform long file names into much shorter ones by keeping an association file to retrieve long info from short codes. 30 | Item((3)) 31 | Using this functionality may imply C(git) conflicts on the association file when several users independently create associations in their repos. 32 | This is fully dealt with and the only thing left to the user is to accept the resolution of the conflict B(without any action). 33 | 34 | Footer 35 | -------------------------------------------------------------------------------- /doc/man/man1/lrepair.1.m: -------------------------------------------------------------------------------- 1 | Comment( 2 | This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | Copyright (c) 2023-2025 Doliam 4 | This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | ) 7 | 8 | Title(lrepair,repair a OpenLmake repo) 9 | .SH SYNOPSYS 10 | B(lrepair) 11 | 12 | .SH DESCRIPTION 13 | .LP 14 | B(lrepair) is meant to repair internal OpenLmake book-keeping in case of catastrophic crash, either because of system failure or a bug in OpenLmake. 15 | Most of the time, OpenLmake does not require to resort to heavy reparation as its internal book-keeping is fairly robust, but in some rare cases, this may be necessary. 16 | In such a case, OpenLmake will suggest the use of B(lrepair). 17 | .LP 18 | In case of incoherent behavior, the user may decide to run B(lrepair). 19 | .LP 20 | This process is pretty long, the goal being to avoid having to restart from a fresh repo, which may incur a severe cost in terms of compute power. 21 | .LP 22 | Once B(lrepair) is done, it generates some suggestions about what to do with the freshly repaired repo, including step back and forget about the repair. 23 | 24 | ClientGeneralities() 25 | 26 | .SH FILES 27 | CommonFiles 28 | 29 | Footer 30 | -------------------------------------------------------------------------------- /doc/man/man1/lrun_cc.1.m: -------------------------------------------------------------------------------- 1 | Comment( 2 | This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | Copyright (c) 2023-2025 Doliam 4 | This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | ) 7 | 8 | Title(lrun_cc,run a C/C++ compiler protecting include dirs) 9 | .SH SYNOPSYS 10 | B(lrun_cc) [-m I(marker)] I(compiler) I(args)... 11 | 12 | .SH DESCRIPTION 13 | .LP 14 | B(lrun_cc) runs the program I(compiler) with any given arguments I(args)... . 15 | I(args) are parsed for include or library dirs and calls mkdir on these dirs and C(ldepend) on I(marker) files within these dirs. 16 | 17 | The goal is to ensure that these dirs always exist as this is a requirement of most C/C++ compiler to try to read include files within them and hence trigger deps. 18 | 19 | Footer 20 | -------------------------------------------------------------------------------- /doc/man/utils.mh: -------------------------------------------------------------------------------- 1 | define(`Comment') 2 | Comment( 3 | This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | Copyright (c) 2023-2025 Doliam 5 | This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | ) 8 | 9 | define(`About',`define(`Name',``$1'')') 10 | 11 | define(`B',`\fB`$1'\fR') 12 | define(`C',`B(`$1')(ifelse(`$2',,1,``$2''))') 13 | define(`I',`\fI`$1'\fR') 14 | define(`Bullet',`.IP \[bu] 15 | ') 16 | define(`Item',`.TP 17 | patsubst(``$*'',`,',`, ') 18 | ') 19 | define(`System',`syscmd(echo -n $(`$1'))') 20 | -------------------------------------------------------------------------------- /doc/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # Summary 7 | 8 | [Overview](overview.md) 9 | [Introduction](intro.md) 10 | [Installation](install.md) 11 | 12 | - [The lmake module](lmake_module.md) 13 | - [lmake.sources](lmake_sources_module.md) 14 | - [lmake.rules](lmake_rules_module.md) 15 | - [lmake.import\_machinery](lmake_import_machinery_module.md) 16 | 17 | - [Writing Lmakefile.py](writing_lmakefile.md) 18 | - [Config fields](config.md) 19 | - [Rules attributes](rules.md) 20 | 21 | - [Execution](execution.md) 22 | - [Job execution](job_execution.md) 23 | - [Data model](data_model.md) 24 | - [Rule selection](rule_selection.md) 25 | - [Backends](backends.md) 26 | - [Namespaces](namespaces.md) 27 | - [Autodep](autodep.md) 28 | - [Critical deps](critical_deps.md) 29 | - [ETA](eta.md) 30 | - [Video mode](video_mode.md) 31 | 32 | - [The LMAKE directory](meta_data.md) 33 | 34 | - [Commands](commands.md) 35 | 36 | - [Experimental features](experimental.md) 37 | - [Cache](experimental_cache.md) 38 | - [Codec](experimental_codec.md) 39 | - [Subrepos](experimental_subrepos.md) 40 | 41 | [Glossary](glossary.md) 42 | [FAQ](faq.md) 43 | -------------------------------------------------------------------------------- /doc/src/experimental.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # Experimental features 7 | 8 | The features described herein are experimental as long as they have not been thoroughly used. 9 | 10 | If you plan to use one of those, best is to be in contact with the development team to: 11 | 12 | - get dedicated support 13 | - make necessary evolutions so as to fit your needs as they appear 14 | 15 | As a consequence, it is most probable that the specifications will evolve in a non-backward compatible way. 16 | -------------------------------------------------------------------------------- /doc/src/experimental_cache.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # Cache 7 | 8 | Several cache mechanisms will be implemented but for now, ony one exists. 9 | 10 | ## DirCache 11 | 12 | This cache is based on a shared dir and requires no running daemon. 13 | 14 | It must be initialized with a file `LMAKE/size` containing the overall size the cache is allowed to occupy. 15 | The value may end with a unit suffix in `k`, `M`, `G`, `T` (powers of 1024). 16 | For example `LMAKE/size` can contain `1.5T`. 17 | -------------------------------------------------------------------------------- /doc/src/experimental_subrepos.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # Sub-repos 7 | 8 | Sub-repos are repos that contain repos, i.e. some `Lmakefile.py` are present in sub-dirs. 9 | 10 | In that situation, it is reasonable to assume that the `Lmakefile.py` are made to handle building files underneath it. 11 | 12 | To support this situation, open-lmake allow you to simply mention such sub-repos, so that: 13 | 14 | - Targets only match within the sub-repo (and escape is possibly by setting the `top` flag to the target to provide global rules). 15 | - The same applies to deps. 16 | - `cmd` is run from this sub-repo, i.e. its cwd is set accordingly. 17 | - The priority of deeper rules are matched first, so that builds in a sub-repo is not pertubated by rules of englobing repo. 18 | -------------------------------------------------------------------------------- /doc/src/faq.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/doc/src/faq.md -------------------------------------------------------------------------------- /doc/src/lmake_sources_module.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # The `lmake.sources` module 7 | 8 | ### `manifest_sources(manifest='Manifest')` 9 | 10 | This function returns the list of sources found in the `manifest` file, one per line. 11 | Comments are supported as everything following a `#` itself at start of line or preceded by a space. 12 | Leading and trailing white spaces are stripped after comment removal. 13 | 14 | ### `git_sources( recurse=True , ignore_missing_submodules=False )` 15 | 16 | This function lists files under `git` control, recursing to sub-modules if `recurse` is true and ignore missing such sub-modules if `ignore_missing_submodules` is true. 17 | 18 | The `git` repo can be an enclosing dir of the open-lmake repo. 19 | In that case, sources are adequately set to track `git` updates. 20 | 21 | ### `auto_sources(**kwds)` 22 | 23 | This function tries to find sources by calling `manifest_sources` and `git_sources` in turn, untill one succeeds. 24 | Arguments are passed as pertinent. 25 | 26 | In absence of source declaration, this function is called with no argument to determine the sources. 27 | -------------------------------------------------------------------------------- /doc/src/video_mode.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # Video mode 7 | 8 | If lmake is connected to a terminal, then the terminal foreground and background colors are probed and if the brightness of the background color is less than that of the foreground color, 9 | video mode is set to normal, else it is set to reverse. 10 | 11 | In that case, lmake output is colored and the (configurable) color set is chosen depending on video mode. 12 | -------------------------------------------------------------------------------- /docker/debian12.docker: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | # build command from top level : sudo docker build -f docker/debian12.docker -t debian12 docker 7 | 8 | FROM debian:12 9 | 10 | RUN apt-get update --fix-missing # 0 : in case of failure, try to increment this number to force docker to reexecute the update line 11 | RUN apt-get install -y build-essential 12 | 13 | # compulsery 14 | RUN apt-get install -y git 15 | RUN apt-get install -y python3-dev 16 | RUN apt-get install -y wget 17 | RUN apt-get install -y m4 groff 18 | # optional 19 | RUN apt-get install -y gdb 20 | RUN apt-get install -y clang 21 | RUN apt-get install -y libpcre2-dev 22 | RUN apt-get install -y libseccomp-dev 23 | RUN apt-get install -y libslurm-dev 24 | RUN apt-get install -y python3-venv 25 | RUN apt-get install -y strace 26 | # 27 | ## necessary to build and install debian package 28 | RUN apt-get install -y dh-make devscripts debhelper equivs apparmor dh-apparmor 29 | RUN apt-get install -y libzstd-dev 30 | RUN apt-get install -y ed 31 | RUN apt-get install -y sudo 32 | 33 | RUN useradd --uid 1001 --home-dir /home/cdy cdy 34 | RUN echo 'cdy ALL = NOPASSWD: ALL' >> /etc/sudoers 35 | USER 1001:1001 36 | WORKDIR /home/cdy 37 | ENV USER=cdy 38 | ENV HOME=/home/cdy 39 | CMD bash 40 | -------------------------------------------------------------------------------- /docker/rocky8.docker: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | # build command from top level : sudo docker build -f docker/rocky8.docker -t rocky8 docker 7 | 8 | FROM rockylinux:8 9 | 10 | RUN dnf update -y 11 | RUN dnf clean all 12 | 13 | # build an intermediate environment 14 | 15 | # compulsery 16 | RUN dnf install -y epel-release 17 | RUN dnf install -y gcc-toolset-11 18 | RUN dnf install -y python3 python3-devel 19 | RUN dnf install -y git 20 | RUN dnf install -y wget 21 | RUN dnf install -y m4 groff 22 | # optional 23 | RUN dnf install -y libseccomp 24 | RUN dnf install -y strace 25 | #RUN dnf install -y glibc-devel.i686 libgcc.i686 gcc-toolset-12-libstdc++-devel.i686 26 | #RUN dnf install -y pcre2 pcre2-devel 27 | #RUN dnf install -y slurm slurm-slurmd slurm-libpmi slurm-munge slurm-devel # does not work 28 | 29 | RUN useradd --uid 1001 --home-dir /home/cdy cdy 30 | USER 1001:1001 31 | WORKDIR /home/cdy 32 | ENV USER=cdy 33 | ENV HOME=/home/cdy 34 | CMD PATH=/opt/rh/gcc-toolset-11/root/usr/bin:$PATH bash 35 | -------------------------------------------------------------------------------- /docker/rocky9.docker: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | # build command from top level : sudo docker build -f docker/rocky9.docker -t rocky9 docker 7 | 8 | FROM rockylinux:9 9 | 10 | RUN dnf update -y 11 | RUN dnf clean all 12 | 13 | # build a maximal environment 14 | 15 | # compulsery 16 | RUN dnf install -y epel-release 17 | RUN dnf install -y gcc gcc-c++ 18 | RUN dnf install -y gcc-toolset-12 19 | RUN dnf install -y python3 python3-devel 20 | RUN dnf install -y git 21 | RUN dnf install -y wget 22 | RUN dnf install -y m4 groff 23 | #optional 24 | RUN dnf install -y libseccomp 25 | RUN dnf install -y strace 26 | RUN dnf install -y glibc-devel.i686 libgcc.i686 gcc-toolset-12-libstdc++-devel.i686 27 | RUN dnf install -y pcre2 pcre2-devel 28 | # RUN dnf install -y slurm slurm-slurmd slurm-libpmi slurm-munge slurm-devel # does not work 29 | 30 | RUN useradd --uid 1001 --home-dir /home/cdy cdy 31 | USER 1001:1001 32 | WORKDIR /home/cdy 33 | ENV USER=cdy 34 | ENV HOME=/home/cdy 35 | CMD PATH=/opt/rh/gcc-toolset-12/root/usr/bin:$PATH bash 36 | -------------------------------------------------------------------------------- /docker/suse156.docker: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | # build command from top level : sudo docker build -f docker/suse156.docker -t suse156 docker 7 | 8 | FROM opensuse/leap:15.6 9 | 10 | RUN zypper refresh # refresh repositories 11 | RUN zypper update -y # update package list 12 | 13 | RUN zypper install -y gcc-c++ make 14 | 15 | # compulsory 16 | RUN zypper install -y gcc13-c++ 17 | RUN zypper install -y git 18 | RUN zypper install -y python3-devel 19 | RUN zypper install -y wget 20 | RUN zypper install -y m4 groff 21 | 22 | # necesary for unit tests 23 | RUN zypper install -y hostname 24 | RUN zypper install -y tar 25 | RUN zypper install -y gzip 26 | 27 | # optional 28 | RUN zypper install -y gdb 29 | RUN zypper install -y clang 30 | RUN zypper install -y pcre2-devel 31 | RUN zypper install -y libseccomp-devel 32 | RUN zypper install -y slurm-devel 33 | RUN zypper install -y strace 34 | RUN zypper install -y sudo 35 | RUN zypper install -y vi 36 | 37 | RUN useradd --uid 1001 --home-dir /home/cdy cdy 38 | RUN echo 'cdy ALL = NOPASSWD: ALL' >> /etc/sudoers 39 | USER 1001:1001 40 | WORKDIR /home/cdy 41 | ENV USER=cdy 42 | ENV HOME=/home/cdy 43 | CMD bash 44 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | This file makes sure that Github Pages doesn't process mdBook's output. 2 | -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/FontAwesome/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/FontAwesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/FontAwesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/FontAwesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/FontAwesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/ayu-highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | Based off of the Ayu theme 3 | Original by Dempfi (https://github.com/dempfi/ayu) 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | overflow-x: auto; 9 | background: #191f26; 10 | color: #e6e1cf; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #5c6773; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-variable, 20 | .hljs-template-variable, 21 | .hljs-attribute, 22 | .hljs-attr, 23 | .hljs-regexp, 24 | .hljs-link, 25 | .hljs-selector-id, 26 | .hljs-selector-class { 27 | color: #ff7733; 28 | } 29 | 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-builtin-name, 33 | .hljs-literal, 34 | .hljs-type, 35 | .hljs-params { 36 | color: #ffee99; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-bullet { 41 | color: #b8cc52; 42 | } 43 | 44 | .hljs-title, 45 | .hljs-built_in, 46 | .hljs-section { 47 | color: #ffb454; 48 | } 49 | 50 | .hljs-keyword, 51 | .hljs-selector-tag, 52 | .hljs-symbol { 53 | color: #ff7733; 54 | } 55 | 56 | .hljs-name { 57 | color: #36a3d9; 58 | } 59 | 60 | .hljs-tag { 61 | color: #00568d; 62 | } 63 | 64 | .hljs-emphasis { 65 | font-style: italic; 66 | } 67 | 68 | .hljs-strong { 69 | font-weight: bold; 70 | } 71 | 72 | .hljs-addition { 73 | color: #91b362; 74 | } 75 | 76 | .hljs-deletion { 77 | color: #d96c75; 78 | } 79 | -------------------------------------------------------------------------------- /docs/css/print.css: -------------------------------------------------------------------------------- 1 | 2 | #sidebar, 3 | #menu-bar, 4 | .nav-chapters, 5 | .mobile-nav-chapters { 6 | display: none; 7 | } 8 | 9 | #page-wrapper.page-wrapper { 10 | transform: none !important; 11 | margin-inline-start: 0px; 12 | overflow-y: initial; 13 | } 14 | 15 | #content { 16 | max-width: none; 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | .page { 22 | overflow-y: initial; 23 | } 24 | 25 | code { 26 | direction: ltr !important; 27 | } 28 | 29 | pre > .buttons { 30 | z-index: 2; 31 | } 32 | 33 | a, a:visited, a:active, a:hover { 34 | color: #4183c4; 35 | text-decoration: none; 36 | } 37 | 38 | h1, h2, h3, h4, h5, h6 { 39 | page-break-inside: avoid; 40 | page-break-after: avoid; 41 | } 42 | 43 | pre, code { 44 | page-break-inside: avoid; 45 | white-space: pre-wrap; 46 | } 47 | 48 | .fa { 49 | display: none !important; 50 | } 51 | -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/favicon.png -------------------------------------------------------------------------------- /docs/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-300.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-300italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-600.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-600italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-700.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-700italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-800.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-800italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/open-sans-v17-all-charsets-regular.woff2 -------------------------------------------------------------------------------- /docs/fonts/source-code-pro-v11-all-charsets-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/docs/fonts/source-code-pro-v11-all-charsets-500.woff2 -------------------------------------------------------------------------------- /docs/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | * An increased contrast highlighting scheme loosely based on the 3 | * "Base16 Atelier Dune Light" theme by Bram de Haan 4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) 5 | * Original Base16 color scheme by Chris Kempson 6 | * (https://github.com/chriskempson/base16) 7 | */ 8 | 9 | /* Comment */ 10 | .hljs-comment, 11 | .hljs-quote { 12 | color: #575757; 13 | } 14 | 15 | /* Red */ 16 | .hljs-variable, 17 | .hljs-template-variable, 18 | .hljs-attribute, 19 | .hljs-attr, 20 | .hljs-tag, 21 | .hljs-name, 22 | .hljs-regexp, 23 | .hljs-link, 24 | .hljs-name, 25 | .hljs-selector-id, 26 | .hljs-selector-class { 27 | color: #d70025; 28 | } 29 | 30 | /* Orange */ 31 | .hljs-number, 32 | .hljs-meta, 33 | .hljs-built_in, 34 | .hljs-builtin-name, 35 | .hljs-literal, 36 | .hljs-type, 37 | .hljs-params { 38 | color: #b21e00; 39 | } 40 | 41 | /* Green */ 42 | .hljs-string, 43 | .hljs-symbol, 44 | .hljs-bullet { 45 | color: #008200; 46 | } 47 | 48 | /* Blue */ 49 | .hljs-title, 50 | .hljs-section { 51 | color: #0030f2; 52 | } 53 | 54 | /* Purple */ 55 | .hljs-keyword, 56 | .hljs-selector-tag { 57 | color: #9d00ec; 58 | } 59 | 60 | .hljs { 61 | display: block; 62 | overflow-x: auto; 63 | background: #f6f7f6; 64 | color: #000; 65 | } 66 | 67 | .hljs-emphasis { 68 | font-style: italic; 69 | } 70 | 71 | .hljs-strong { 72 | font-weight: bold; 73 | } 74 | 75 | .hljs-addition { 76 | color: #22863a; 77 | background-color: #f0fff4; 78 | } 79 | 80 | .hljs-deletion { 81 | color: #b31d28; 82 | background-color: #ffeef0; 83 | } 84 | -------------------------------------------------------------------------------- /examples/cc.dir/Manifest: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | # in a real world, repository is managed by source version control tool such as git and this file is not necessary 7 | 8 | Manifest # Manifest itself is a source 9 | Lmakefile.py # as well as Lmakefile.py 10 | 11 | hello.h 12 | hello.c 13 | world.h 14 | world.c 15 | hello_world.c 16 | hello_world.regr 17 | -------------------------------------------------------------------------------- /examples/cc.dir/hello.c: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include "hello.h" 7 | 8 | const char* hello() { 9 | return "hello" ; 10 | } 11 | -------------------------------------------------------------------------------- /examples/cc.dir/hello.h: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | const char* hello() ; 7 | -------------------------------------------------------------------------------- /examples/cc.dir/hello_world.c: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include 7 | #include 8 | 9 | #include "hello.h" 10 | #include "world.h" 11 | 12 | int main( int argc , const char* argv[] ) { 13 | if (argc>2) { 14 | fprintf(stderr,"%s accepts at most a single argument, %d given\n",argv[0],argc-1) ; 15 | exit(2) ; 16 | } 17 | printf("%s %s\n",hello(),world(argv[1])) ; 18 | return 0 ; 19 | } 20 | -------------------------------------------------------------------------------- /examples/cc.dir/hello_world.regr: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | # test : scenario : expected output 7 | smith : Mr Smith : hello Mr Smith 8 | world : : hello world 9 | -------------------------------------------------------------------------------- /examples/cc.dir/world.c: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include "world.h" 7 | 8 | const char* world(const char* w) { 9 | return w&&*w ? w : "world" ; 10 | } 11 | -------------------------------------------------------------------------------- /examples/cc.dir/world.h: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | const char* world(const char*) ; 7 | -------------------------------------------------------------------------------- /examples/cpp20_modules.dir/Manifest: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | # in a real world, repository is managed by source version control tool such as git and this file is not necessary 7 | 8 | Manifest # Manifest itself is a source 9 | Lmakefile.py # as well as Lmakefile.py 10 | 11 | Lmakefile.py 12 | another.mpp 13 | duplicate.mpp 14 | run 15 | use.mpp 16 | -------------------------------------------------------------------------------- /examples/cpp20_modules.dir/another.mpp: -------------------------------------------------------------------------------- 1 | // derived from https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html 2 | // only difference is the addition of _m to module names to show they are independent from file names 3 | 4 | export module another_m; 5 | import duplicate_m; 6 | 7 | export int i() { 8 | return m(); 9 | } 10 | -------------------------------------------------------------------------------- /examples/cpp20_modules.dir/duplicate.mpp: -------------------------------------------------------------------------------- 1 | // derived from https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html 2 | // only difference is the addition of _m to module names to show they are independent from file names 3 | 4 | export module duplicate_m; 5 | 6 | export int m() { 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /examples/cpp20_modules.dir/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | # Copyright (c) 2023-2025 Doliam 4 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | 7 | case $(g++ --version | awk 'NR==1 {print $NF}') in 8 | 13*) ;; 9 | 14*) ;; 10 | * ) echo gcc version is neither version 13 nor 14 >skipped ; exit 0 ;; 11 | esac 12 | 13 | lmake use.o 14 | -------------------------------------------------------------------------------- /examples/cpp20_modules.dir/use.mpp: -------------------------------------------------------------------------------- 1 | // derived from https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html 2 | // only difference is the addition of _m to module names to show they are independent from file names 3 | 4 | import duplicate_m; 5 | import another_m; 6 | 7 | int lib() { 8 | return m() + i(); 9 | } 10 | -------------------------------------------------------------------------------- /examples/hello_world.dir/hello: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /examples/hello_world.dir/world: -------------------------------------------------------------------------------- 1 | world 2 | -------------------------------------------------------------------------------- /lmake_env/ext_lnk: -------------------------------------------------------------------------------- 1 | ext -------------------------------------------------------------------------------- /museum/src/fuse_test.cc: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | // XXXM : fuse autodep method is under construction 7 | 8 | #include "disk.hh" 9 | 10 | #include "fuse.hh" 11 | 12 | using namespace Disk ; 13 | 14 | int main( int /*argc*/ , char* /*argv*/[] ) { 15 | t_thread_key = '=' ; 16 | ::cerr< depend ( Record const& , ::vector_s&& files , AccessDigest , bool no_follow , bool verbose=false , bool regexpr=false ) ; 14 | void target ( Record const& , ::vector_s&& files , AccessDigest , bool regexpr=false ) ; 15 | Bool3 check_deps( Record const& , bool sync=false ) ; 16 | ::pair_s decode ( Record const& , ::string&& file , ::string&& code , ::string&& ctx ) ; 17 | ::pair_s encode ( Record const& , ::string&& file , ::string&& val , ::string&& ctx , uint8_t min_len=1 ) ; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/autodep/lcheck_deps.cc: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include "app.hh" 7 | 8 | #include "job_support.hh" 9 | #include "record.hh" 10 | 11 | enum class Key : uint8_t { None } ; 12 | 13 | enum class Flag : uint8_t { 14 | Sync 15 | } ; 16 | 17 | int main( int argc , char* argv[]) { 18 | Syntax syntax {{ 19 | { Flag::Sync , { .short_name='s' , .doc="wait for server reply that previous deps are up-to-date with no error" } } 20 | }} ; 21 | CmdLine cmd_line { syntax , argc , argv } ; if (cmd_line.args.size()!=0 ) syntax.usage("must have no argument") ; 22 | bool sync = cmd_line.flags[Flag::Sync] ; 23 | Bool3 ok = JobSupport::check_deps( {New,Yes/*enabled*/} , sync ) ; 24 | if (!sync) return 0 ; 25 | else return ok!=Yes ; 26 | } 27 | -------------------------------------------------------------------------------- /src/autodep/ld_preload.cc: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #define LD_PRELOAD 1 7 | 8 | #include "utils.hh" 9 | 10 | inline bool started() { return true ; } 11 | 12 | void* get_orig(const char* libcall) { 13 | void* res = ::dlsym(RTLD_NEXT,libcall) ; 14 | swear_prod(res,"cannot find symbol",libcall,"in libc") ; 15 | return res ; 16 | } 17 | 18 | #include "ld_common.x.cc" 19 | -------------------------------------------------------------------------------- /src/autodep/ld_server.hh: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include "record.hh" 7 | 8 | #pragma once 9 | 10 | Record& auditor() ; 11 | 12 | struct AutodepLock { 13 | // static data 14 | static thread_local bool t_active ; 15 | private : 16 | static Mutex _s_mutex ; 17 | // cxtors & casts 18 | public : 19 | AutodepLock( ) = default ; 20 | AutodepLock(::vmap_s* deps=nullptr) ; 21 | // 22 | ~AutodepLock() ; 23 | // data 24 | Lock> lock ; 25 | ::string err ; 26 | } ; 27 | -------------------------------------------------------------------------------- /src/lforget.cc: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include "app.hh" 7 | #include "client.hh" 8 | #include "rpc_client.hh" 9 | 10 | int main( int argc , char* argv[] ) { 11 | app_init(false/*read_only_ok*/) ; 12 | Trace trace("main") ; 13 | // 14 | ReqSyntax syntax {{ 15 | { ReqKey::None , { .short_name=0 , .doc="rerun files provided in arguments" } } 16 | , { ReqKey::Resources , { .short_name='r' , .doc="rerun jobs with new resources, even if not in error" } } 17 | },{ 18 | { ReqFlag::Deps , { .short_name='d' , .doc="forget about deps" } } 19 | , { ReqFlag::Targets , { .short_name='t' , .doc="forget about targets" } } 20 | }} ; 21 | ReqCmdLine cmd_line{syntax,argc,argv} ; 22 | // 23 | bool refresh = cmd_line.key==ReqKey::Resources ; 24 | if ( refresh && +cmd_line.args ) syntax.usage("must not have targets when forgetting resources" ) ; 25 | // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 26 | Bool3 ok = out_proc( ReqProc::Forget , false/*read_only*/ , refresh , syntax , cmd_line ) ; 27 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 28 | exit(mk_rc(ok)) ; 29 | } 30 | -------------------------------------------------------------------------------- /src/lib.hh: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #pragma once 7 | 8 | #include "utils.hh" 9 | 10 | enum class LnkSupport : uint8_t { 11 | None 12 | , File 13 | , Full 14 | } ; 15 | 16 | struct SearchRootResult { 17 | ::string top_s ; 18 | ::string sub_s ; 19 | ::string startup_s ; 20 | } ; 21 | 22 | SearchRootResult search_root(::string const& cwd_s={}) ; // use cwd_s() by default 23 | -------------------------------------------------------------------------------- /src/lmakeserver/cmd.hh: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include "core.hh" // /!\ must be first to include Python.h first 7 | 8 | namespace Engine { 9 | 10 | using CmdFunc = bool (*)(EngineClosureReq const&) ; 11 | extern CmdFunc g_cmd_tab[N] ; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/lmakeserver/core.hh: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #pragma once 7 | 8 | #include "py.hh" // /!\ must be first as Python.h must be first 9 | 10 | #include "types.hh" 11 | 12 | #include "disk.hh" 13 | #include "hash.hh" 14 | #include "msg.hh" 15 | #include "process.hh" 16 | #include "thread.hh" 17 | #include "time.hh" 18 | #include "trace.hh" 19 | 20 | namespace Engine { 21 | using FileSig = Disk::FileSig ; 22 | using Crc = Hash::Crc ; 23 | using Delay = Time::Delay ; 24 | using CoarseDelay = Time::CoarseDelay ; 25 | using Ddate = Time::Ddate ; 26 | using Pdate = Time::Pdate ; 27 | using SigDate = Disk::SigDate ; 28 | } 29 | 30 | #define STRUCT_DECL 31 | #include "core.x.hh" 32 | #undef STRUCT_DECL 33 | 34 | #define STRUCT_DEF 35 | #include "core.x.hh" 36 | #undef STRUCT_DEF 37 | 38 | #define INFO_DEF 39 | #include "core.x.hh" 40 | #undef INFO_DEF 41 | 42 | #define DATA_DEF 43 | #include "core.x.hh" 44 | #undef DATA_DEF 45 | 46 | #define IMPL 47 | #include "core.x.hh" 48 | #undef IMPL 49 | -------------------------------------------------------------------------------- /src/lmakeserver/core.x.hh: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | // included 5 times : with STRUCT_DECL defined, then with STRUCT_DEF defined, then with INFO_DEF defined, then with DATA_DEF defined, then with IMPL defined 7 | 8 | #include "global.x.hh" 9 | #include "store.x.hh" 10 | #include "config.x.hh" 11 | #include "backend.x.hh" 12 | #include "rule.x.hh" 13 | #include "req.x.hh" 14 | #include "job.x.hh" 15 | #include "node.x.hh" 16 | -------------------------------------------------------------------------------- /src/lmakeserver/makefiles.hh: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include "time.hh" 7 | 8 | #pragma once 9 | 10 | namespace Engine::Makefiles { 11 | void refresh ( ::string&/*out*/ msg , bool chk, bool refresh ) ; 12 | void dyn_refresh( ::string&/*out*/ msg , ::umap_ss const& env , ::string const& startup_dir_s ) ; // startup dir for diag purpose only 13 | } 14 | -------------------------------------------------------------------------------- /src/non_portable.hh: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #pragma once 7 | 8 | #include "std.hh" 9 | 10 | static constexpr char NpErrnoSymbolName[] = "__errno_location" ; // XXX! : find a way to stick to documented interfaces 11 | 12 | #if __x86_64__ || __aarch64__ 13 | static constexpr uint8_t NpWordSz = 64 ; 14 | #elif __i386__ || __arm__ 15 | static constexpr uint8_t NpWordSz = 32 ; 16 | #endif 17 | 18 | ::array np_ptrace_get_args( pid_t pid , uint8_t word_sz ) ; // word_sz must be 32 or 64 19 | int64_t np_ptrace_get_res ( pid_t pid , uint8_t word_sz ) ; // . 20 | long np_ptrace_get_nr ( pid_t pid , uint8_t word_sz ) ; // . 21 | void np_ptrace_set_res ( pid_t pid , int64_t val , uint8_t word_sz ) ; // . 22 | -------------------------------------------------------------------------------- /src/store/big_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | import random 4 | import subprocess as sp 5 | import sys 6 | 7 | def prepare_files(dir,n_files) : 8 | files = {'Manifest','Lmakefile.py'} 9 | dir = osp.abspath(dir) 10 | l = len(dir) 11 | if dir[-1]!='/' : l += 1 12 | for dp,ds,fs in os.walk(dir) : 13 | if dp[-1]!='/' : dp+= '/' 14 | dp = dp[l:] 15 | if dp : os.makedirs(dp,exist_ok=True) 16 | for f in fs : 17 | f = dp+f 18 | try : f.encode() 19 | except : continue 20 | if f not in files : 21 | open(f,'w') 22 | files.add(f) 23 | f += '.XXXXXXXXXXXXXXXXX' 24 | if f not in files : 25 | open(f,'w') 26 | files.add(f) 27 | n = len(files) 28 | if n%1000==0 : print('.',end='',flush=True) 29 | if n>=n_files : return files 30 | return files 31 | 32 | # use a dir as a source of files 33 | print(f'preparing {sys.argv[2]} files from {sys.argv[1]} ',end='',flush=True) 34 | files = prepare_files( sys.argv[1] , int(sys.argv[2]) ) 35 | print(' done') 36 | 37 | # random shuffle 38 | print(f'shuffling Manifest ...',end='',flush=True) 39 | files = list(files) 40 | random.shuffle(files) 41 | print(' done') 42 | 43 | # add necessary files 44 | open('Lmakefile.py','w') 45 | print(f'writing Manifest ...',end='',flush=True) 46 | with open('Manifest','w') as m : 47 | for f in files : print(f,file=m) 48 | print(f' done {len(files)} files') 49 | 50 | # clean up 51 | print(f'cleaning repo ...',end='',flush=True) 52 | sp.run(('rm','-f','LMAKE'),check=True) 53 | print(' done') 54 | 55 | # read makefile 56 | print(f'running lmake ...') 57 | sp.run(('time','lmake'),check=True) 58 | print('done') 59 | 60 | # check db 61 | print(f'checking lmake store ...',end='',flush=True) 62 | sp.run(('ldump',),check=True,stdout=sp.DEVNULL) 63 | print(' done') 64 | -------------------------------------------------------------------------------- /src/xxhsum.cc: -------------------------------------------------------------------------------- 1 | // This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | // Copyright (c) 2023-2025 Doliam 3 | // This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | #include 7 | #include 8 | 9 | #include "app.hh" 10 | #include "disk.hh" 11 | #include "hash.hh" 12 | 13 | using namespace Disk ; 14 | using namespace Hash ; 15 | 16 | int main( int argc , char* argv[] ) { 17 | 18 | app_init(true/*read_only_ok*/,No/*chk_version*/,No/*cd_root*/) ; 19 | #if PROFILING 20 | ::string gmon_dir_s ; if (g_repo_root_s) gmon_dir_s = *g_repo_root_s+AdminDirS+"gmon.out/" ; 21 | set_env( "GMON_OUT_PREFIX" , dir_guard(gmon_dir_s+"xxhsum") ) ; // in case profiling is used, ensure unique gmon.out 22 | #endif 23 | ::string out ; 24 | for( int i : iota(1,argc) ) { 25 | /**/ out << ::string(Crc(argv[i])) ; 26 | if (argc>2) out <<' '<< argv[i] ; 27 | /**/ out <<'\n' ; 28 | } 29 | Fd::Stdout.write(out) ; 30 | return 0 ; 31 | } 32 | -------------------------------------------------------------------------------- /unit_tests/abort.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'dut.c' 14 | ) 15 | 16 | class Compile(Rule) : 17 | targets = { 'EXE' : r'{File:.*}.exe' } 18 | deps = { 'SRC' : '{File}.c' } 19 | cmd = 'gcc -std=c99 -o {EXE} {SRC}' 20 | 21 | class Dut(Rule) : 22 | target = r'{File:.*}.out' 23 | deps = { 'EXE':'{File}.exe' } 24 | tmp_view = '/tmp' 25 | cmd = './{EXE}' 26 | 27 | else : 28 | 29 | from lmake import multi_strip 30 | import ut 31 | 32 | open('dut.c','w').write(multi_strip(''' 33 | #include 34 | int main() { 35 | abort() ; 36 | } 37 | ''')) 38 | 39 | ut.lmake( 'dut.out' , new=1 , done=1 , failed=1 , rc=1 ) 40 | -------------------------------------------------------------------------------- /unit_tests/admin.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | lmake.config.local_admin_dir = 'LMAKE_LOCAL' 14 | lmake.config.remote_admin_dir = 'LMAKE_REMOTE' 15 | 16 | class Test(Rule) : 17 | target = 'test' 18 | cmd = '' 19 | 20 | else : 21 | 22 | import ut 23 | 24 | ut.lmake( 'test' , done=1 ) 25 | -------------------------------------------------------------------------------- /unit_tests/back_ref.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'dep' 14 | ) 15 | 16 | class Cat(Rule) : 17 | targets = { 'TGT' : r'{*:.*}+{File:.*}+{D*:.*}+{*:.*}+{D*}+{File}' } 18 | deps = { 'FIRST' : '{File}' } 19 | cmd = "echo {File} $(cat {FIRST}) >{TGT('single1','double','single2')}" 20 | 21 | class Test(Rule) : 22 | target = 'test' 23 | dep = 'single1+dep+double+single2+double+dep' 24 | cmd = '[ "$(cat)" = "dep hello" ]' 25 | 26 | else : 27 | 28 | import os 29 | import os.path as osp 30 | 31 | import ut 32 | 33 | print('hello',file=open('dep','w')) 34 | 35 | ut.lmake( 'test' , done=2 , new=1 ) 36 | -------------------------------------------------------------------------------- /unit_tests/base/hello.py: -------------------------------------------------------------------------------- 1 | def hello(x) : 2 | return f'hello {x}' 3 | -------------------------------------------------------------------------------- /unit_tests/base/src1: -------------------------------------------------------------------------------- 1 | #src1 2 | -------------------------------------------------------------------------------- /unit_tests/base/src2: -------------------------------------------------------------------------------- 1 | #src2 2 | -------------------------------------------------------------------------------- /unit_tests/calc2.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'src1' 14 | , 'src2' 15 | , 'src1+src2.ref' 16 | ) 17 | 18 | def balanced(n) : 19 | if not n : return r'[^{}]*' 20 | p = balanced(n-1) 21 | return fr'{p}(\{{{p}\}}{p})*' 22 | class BaseRule(Rule) : 23 | stems = { 24 | 'File' : r'.*' 25 | , 'SubExpr' : balanced(0) 26 | , 'Expr' : balanced(1) 27 | , 'Digit' : r'\d' 28 | } 29 | stems['Expr1'] = stems['Expr'] 30 | stems['Expr2'] = stems['Expr'] 31 | shell = Rule.shell + ('-e',) 32 | 33 | class Cat(BaseRule) : 34 | target = '{Expr1}+{Expr2}' 35 | deps = { 36 | 'FIRST' : '{Expr1}' 37 | , 'SECOND' : '{Expr2}' 38 | } 39 | cmd = 'cat {FIRST} {SECOND}' 40 | 41 | class Cmp(BaseRule) : 42 | target = '{File}.ok' 43 | deps = { 44 | 'DUT' : '{File}' 45 | , 'REF' : '{File}.ref' 46 | } 47 | cmd = 'diff {REF} {DUT}' 48 | 49 | class Cpy(BaseRule) : 50 | target = '{File}.cpy' 51 | dep = '{File}' 52 | cmd = 'cat' 53 | 54 | else : 55 | 56 | import ut 57 | 58 | print('#src1' ,file=open('src1' ,'w')) 59 | print('#src2' ,file=open('src2' ,'w')) 60 | print('#src1\n#src2',file=open('src1+src2.ref','w')) 61 | 62 | ut.lmake( 'src1+src2.ok.cpy' , done=3 , new=3 ) 63 | -------------------------------------------------------------------------------- /unit_tests/callables.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | def config() : 9 | pass 10 | 11 | def sources() : 12 | import lmake 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'hello' 16 | , 'world' 17 | ) 18 | 19 | def rules() : 20 | # 21 | from lmake.rules import Rule,PyRule 22 | # 23 | class Cat(Rule) : 24 | stems = { 25 | 'File1' : r'.*' 26 | , 'File2' : r'.*' 27 | } 28 | deps = { 29 | 'FIRST' : '{File1}' 30 | , 'SECOND' : '{File2}' 31 | } 32 | # 33 | class CatSh(Cat) : 34 | target = '{File1}+{File2}_sh' 35 | cmd = 'cat {FIRST} {SECOND}' 36 | # 37 | class CatPy(Cat,PyRule) : 38 | target = '{File1}+{File2}_py' 39 | def cmd() : 40 | print(open(FIRST ).read(),end='') 41 | print(open(SECOND).read(),end='') 42 | 43 | else : 44 | 45 | import os 46 | 47 | import ut 48 | 49 | print('hello',file=open('hello','w')) 50 | print('world',file=open('world','w')) 51 | 52 | ut.lmake( 'hello+world_sh' , 'hello+world_py' , done=2 , new=2 ) # check targets are out of date 53 | ut.lmake( 'hello+world_sh' , 'hello+world_py' , done=0 , new=0 ) # check targets are up to date 54 | ut.lmake( 'hello+hello_sh' , 'world+world_py' , done=2 ) # check reconvergence 55 | -------------------------------------------------------------------------------- /unit_tests/chain.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'hello' 14 | , 'world' 15 | , 'hello.cpy+world.ref' 16 | ) 17 | 18 | class BaseRule(Rule) : 19 | stems = { 'File' : r'.*' } 20 | stems['File1'] = stems['File'] 21 | stems['File2'] = stems['File'] 22 | 23 | class Cat(BaseRule) : 24 | target = '{File1}+{File2}' 25 | deps = { 26 | 'FIRST' : '{File1}' 27 | , 'SECOND' : '{File2}' 28 | } 29 | cmd = 'cat {FIRST} {SECOND}' 30 | 31 | class Cpy(BaseRule) : 32 | target = r'{File}.cpy' 33 | dep = '{File}' 34 | cmd = 'cat' 35 | 36 | class Cmp(BaseRule) : 37 | target = '{File}.ok' 38 | deps = { 39 | 'DUT' : '{File}' 40 | , 'REF' : '{File}.ref' 41 | } 42 | cmd = 'diff {REF} {DUT}' 43 | 44 | else : 45 | 46 | import ut 47 | 48 | print( 'hello' , file=open('hello' ,'w') ) 49 | print( 'world' , file=open('world' ,'w') ) 50 | print( 'hello\nworld' , file=open('hello.cpy+world.ref','w') ) 51 | 52 | ut.lmake( 'hello.cpy+world.ok' , done=3 , new=3 ) 53 | -------------------------------------------------------------------------------- /unit_tests/chdir.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | from lmake import multi_strip 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'step.py' 15 | ) 16 | 17 | from step import step 18 | 19 | class NewDep(Rule) : 20 | target = r'new_dir/dep{Digit:\d}' 21 | cmd = 'echo dep{Digit}' 22 | 23 | class Chdir(Rule) : 24 | target = r'chdir{Digit:\d}' 25 | auto_mkdir = step==2 26 | shell = Rule.shell + ('-e',) 27 | cmd = multi_strip(''' 28 | cd new_dir 29 | cat dep{Digit} 30 | ''') 31 | 32 | else : 33 | 34 | import ut 35 | 36 | print('step=1',file=open('step.py','w')) 37 | ut.lmake( 'chdir1' , failed=1 , rc=1 ) 38 | 39 | print('step=2',file=open('step.py','w')) 40 | ut.lmake( 'chdir1' , done=2 , may_rerun=1 ) 41 | -------------------------------------------------------------------------------- /unit_tests/check_deps.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Dut(Rule) : 14 | target = 'dut' 15 | cmd = 'cat dep ; lcheck_deps ; sleep 1' # sleep 1 to ensure job is killed when lcheck_deps fails 16 | 17 | class Dep(Rule) : 18 | target = 'dep' 19 | cmd = '' 20 | 21 | else : 22 | 23 | import ut 24 | 25 | ut.lmake( 'dep' , done=1 ) 26 | ut.lmake( 'dut' , done=1 ) # ensure no rerun 27 | -------------------------------------------------------------------------------- /unit_tests/clone.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | import gxx 12 | 13 | ld_library_path = lmake._find_cc_ld_library_path(gxx.gxx) 14 | 15 | lmake.manifest = ( 16 | 'Lmakefile.py' 17 | , 'gxx.py' 18 | , 'dut.cc' 19 | , 'dep' 20 | ) 21 | 22 | class Compile(Rule) : 23 | targets = { 'EXE' : r'{File:.*}.exe' } 24 | deps = { 'SRC' : '{File }.cc' } 25 | autodep = 'ld_preload' # clang seems to be hostile to ld_audit 26 | cmd = f'PATH={gxx.gxx_dir}:$PATH {gxx.gxx} -O0 -fdiagnostics-color=always -std=c++20 -pthread -o {{EXE}} {{SRC}}' 27 | 28 | class Dut(Rule) : 29 | target = r'{File:.*}.out' 30 | deps = { 'EXE':'{File}.exe' } 31 | environ = { 'LD_LIBRARY_PATH' : ld_library_path } 32 | cmd = './{EXE}' 33 | 34 | else : 35 | 36 | import os 37 | 38 | import ut 39 | 40 | ut.mk_gxx_module('gxx') 41 | 42 | os.symlink('../clone.cc','dut.cc') 43 | 44 | print('1',file=open('dep','w')) 45 | ut.lmake( 'dut.out' , new=2 , done=2 ) 46 | 47 | print('2',file=open('dep','w')) 48 | ut.lmake( 'dut.out' , changed=1 , done=1 ) # ensure dep is seen as a dep 49 | -------------------------------------------------------------------------------- /unit_tests/cmd.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Base(PyRule) : 14 | def cmd() : return 1 15 | 16 | class Dut(Base) : 17 | target = 'dut' 18 | def cmd(val) : print(val) 19 | 20 | class Tst(Rule) : 21 | target = 'tst' 22 | dep = 'dut' 23 | cmd = '[ $(cat) = 1 ]' 24 | 25 | else : 26 | 27 | import ut 28 | 29 | ut.lmake( f'tst' , done=2 ) 30 | -------------------------------------------------------------------------------- /unit_tests/conflict.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Tmp(Rule) : # tmp is used both as a plain dep for B and as a scratchpad for A 14 | target = 'tmp' 15 | cmd = '' 16 | 17 | class A(Rule) : 18 | target = 'a' 19 | cmd = 'echo >tmp ; rm tmp ; exit 0' 20 | 21 | class B(Rule) : 22 | target = 'b' 23 | dep = 'a' 24 | cmd = 'cat tmp a' 25 | 26 | else : 27 | 28 | import ut 29 | 30 | ut.lmake( 'tmp' , done=1 ) # ensure tmp exists 31 | # 32 | cnt = ut.lmake( 'b' , may_rerun=1 , done=2 , rerun=... , was_done=1 ) # tmp is unlinked by a, then regenerated for b 33 | assert cnt.rerun<=1,f'bad rerun count {cnt.rerun}' # tmp may be too new for b to be done right away 34 | -------------------------------------------------------------------------------- /unit_tests/conflict2.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import PyRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class A(PyRule) : 14 | target = 'a' 15 | def cmd(): pass 16 | 17 | class B(PyRule) : 18 | targets = { 'B' : 'b' } 19 | side_deps = { 'A' : (r'a{*:.*}','Ignore') } 20 | def cmd(): 21 | open(A('') ) 22 | open(B ,'w') 23 | 24 | else : 25 | 26 | import ut 27 | ut.lmake( 'b',new=1,failed=1,rc=1) # a does not exist 28 | ut.lmake( 'a', done =1 ) 29 | ut.lmake( 'b', done =0,rc=1) # b is up to date 30 | ut.lmake('-e','b', done =1 ) # b is remade because it was in error 31 | ut.lmake( 'a' ) # check a is not remade because b ran 32 | -------------------------------------------------------------------------------- /unit_tests/critical2.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'lst' 14 | ) 15 | 16 | class GenFile(Rule) : 17 | target = r'dep{Digit:\d+}' 18 | cmd = '' 19 | 20 | 21 | class GenAfter(Rule) : 22 | target = r'after{Digit:\d+}' 23 | cmd = '' 24 | 25 | class All(PyRule) : 26 | target = 'dut' 27 | deps = { 28 | 'LST' : ('lst' ,'Critical') 29 | , 'AFTER' : 'after2' 30 | } 31 | def cmd() : 32 | from lmake import depend 33 | for t in open(LST).readlines() : 34 | depend(t.strip()) 35 | open('after1') 36 | open('after2') 37 | open('after3') 38 | 39 | else : 40 | 41 | import os 42 | 43 | import ut 44 | 45 | print('dep1',file=open('lst','w')) ; ut.lmake( 'dut' , new =2 , may_rerun=2 , done=5 ) 46 | print('dep2',file=open('lst','a')) ; ut.lmake( 'dut' , changed=1 , may_rerun=1 , done=1 , was_steady=1 ) 47 | os.unlink('after1') 48 | os.unlink('after2') 49 | os.unlink('after3') 50 | print('dep1',file=open('lst','w')) 51 | ut.lmake( 'dut' , changed=1 , may_rerun=2 , steady=4 ) # check after2 is built despite lst being modified 52 | -------------------------------------------------------------------------------- /unit_tests/cycle.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | from step import step 9 | 10 | import lmake 11 | from lmake.rules import Rule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'step.py' 16 | ) 17 | 18 | class A(Rule) : 19 | target = 'a' 20 | if step==1 : cmd = 'cat b/c' 21 | else : cmd = '' 22 | 23 | class B(Rule) : 24 | target = 'b' 25 | cmd = 'cat a' 26 | 27 | class C1(Rule) : 28 | target = 'c1' 29 | dep = 'c2' 30 | cmd = 'cat' 31 | 32 | class C2(Rule) : 33 | target = 'c2' 34 | dep = 'c1' 35 | cmd = 'cat' 36 | 37 | class D(Rule) : 38 | target = 'd' 39 | dep = 'd' 40 | cmd = 'cat' 41 | 42 | else : 43 | 44 | import subprocess as sp 45 | 46 | import ut 47 | 48 | print('step=1',file=open('step.py','w')) 49 | ut.lmake( 'b' , may_rerun=2 , rc=1 ) 50 | 51 | print('step=2',file=open('step.py','w')) 52 | ut.lmake( 'b' , steady=0 , rc=1 ) # loop is solved, but lmake cant cope with the situation 53 | sp.run(('lforget','-d','a'),check=True) # follow recommandation 54 | ut.lmake( 'b' , steady=2 , rc=0 ) # loop is solved 55 | 56 | ut.lmake( 'c1' , rc=1 ) 57 | ut.lmake( 'd' , rc=1 ) 58 | -------------------------------------------------------------------------------- /unit_tests/dangling.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Dep(PyRule) : 14 | targets = { 'OUT' : r'deps/{*:.*}' } 15 | cmd = '' 16 | 17 | class Dut(Rule): 18 | target = 'dut' 19 | cmd = 'cat deps/dangling' 20 | 21 | else : 22 | 23 | import os 24 | 25 | import ut 26 | 27 | ut.lmake( 'deps/dangling' , steady=1 , rc=1 ) 28 | 29 | os.makedirs('deps',exist_ok=True) 30 | open('deps/dangling','w') 31 | 32 | ut.lmake( 'dut' , dangling=1 , dep_error=1 , rc=1 ) 33 | -------------------------------------------------------------------------------- /unit_tests/depend2.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'ref' 14 | ) 15 | 16 | 17 | class Dep(Rule) : 18 | targets = { 'DEP' : 'dep' } 19 | cmd = 'echo line1 > {DEP} ; sleep 2 ; echo line2 >> {DEP}' 20 | 21 | class Dut(Rule) : 22 | target = 'dut' 23 | cmd = 'ldepend dep ; sleep 1 ; cat dep ; sleep 2 ; cat dep' # dep is required, done, and moving while dut is running 24 | 25 | class Chk(Rule) : 26 | target = 'chk' 27 | deps = { 28 | 'DUT' : 'dut' 29 | , 'REF' : 'ref' 30 | } 31 | cmd = 'diff {REF} {DUT}' 32 | 33 | else : 34 | 35 | import ut 36 | 37 | print('line1\nline2\nline1\nline2',file=open('ref','w')) 38 | 39 | ut.lmake( 'dep' , 'chk' , new=1 , rerun=1 , done=3 ) # build dep so that it appear during dut run 40 | -------------------------------------------------------------------------------- /unit_tests/dup.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py','src') 12 | 13 | n = 10 14 | 15 | class Dut(Rule) : 16 | targets = { f'T{i}' : f'dut_{i}' for i in range(3,n) } 17 | cmd = ' '.join(( 18 | '(' 19 | , *(f'echo "$(cat src){i}" >&{i} ;' for i in range(3,n)) 20 | , ')' 21 | , *(f'{i}>dut_{i}' for i in range(3,n)) 22 | )) 23 | 24 | class Chk(Rule) : 25 | target = 'test' 26 | deps = { f'D{i}' : f'dut_{i}' for i in range(3,n) } 27 | shell = ('/bin/bash','-e') 28 | cmd = '\n'.join( f'[ "$(cat dut_{i})" = "$(cat src){i}" ]' for i in range(3,n) ) 29 | 30 | else : 31 | 32 | import ut 33 | 34 | print('hello',file=open('src','w')) 35 | 36 | ut.lmake( 'test' , done=2 , new=1 ) # check we can duplicate file descriptors with dup2 37 | -------------------------------------------------------------------------------- /unit_tests/dyn_accesses.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | import os 11 | 12 | from lmake.rules import Rule,PyRule 13 | 14 | lmake.manifest = ( 15 | 'Lmakefile.py' 16 | , 'one' 17 | , 'two' 18 | , 'step.py' 19 | ) 20 | 21 | from step import step 22 | 23 | class Deps(PyRule) : 24 | target = 'deps' 25 | def deps() : 26 | if step==1 : open('one') 27 | return {} 28 | def cmd() : pass 29 | 30 | class Resources(Rule) : 31 | target = r'resources' 32 | deps = { 'ONE' : 'one' } 33 | if step==1 : resources = { 'cpu' : "{open(ONE ).read().strip()}" } # static dep 34 | else : resources = { 'cpu' : "{open('two').read().strip()}" } # hidden dep 35 | cmd = '[ {cpu} = {step} ] && echo {cpu}' 36 | 37 | class Env(Rule) : 38 | target = r'env' 39 | deps = { 'ONE' : 'one' } 40 | if step==1 : environ = { 'VAR' : "{open(ONE ).read().strip()}" } # static dep 41 | else : environ = { 'VAR' : "{open('two').read().strip()}" } # hidden dep 42 | cmd = '[ $VAR = {step} ] && echo $VAR' 43 | 44 | else : 45 | 46 | import ut 47 | 48 | print(1,file=open('one','w')) 49 | print(2,file=open('two','w')) 50 | 51 | print('step=1',file=open('step.py','w')) 52 | ut.lmake( 'deps','resources','env' , new=1 , deps_not_avail=1 , done=2 , rc=1 ) # resources can have dynamic deps 53 | 54 | print('step=2',file=open('step.py','w')) 55 | ut.lmake( 'deps','resources','env' , new=1 , done=3 ) 56 | -------------------------------------------------------------------------------- /unit_tests/dyn_config.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | echo 'import lmake' >> Lmakefile.py 9 | echo 'lmake.config.console.date_precision=1' >> Lmakefile.py 10 | 11 | ut_launch -done 2 -new 1 lmake -s src1.8r.wait & # use -s option to be sure lmakeserver is done when lmake is done 12 | 13 | sleep 3 14 | 15 | echo 'lmake.config.console.date_precision=2' >> Lmakefile.py 16 | ut_launch -done 2 lmake src1.cpy # this is ok, config modif can be done dynamically 17 | 18 | sleep 1 19 | 20 | echo new_src > new_src # create new source 21 | echo new_src >>Manifest # and tell lmake 22 | ut_launch -done 0 -rc 1 lmake new_src # cannot dynamically modify sources 23 | 24 | wait %1 # -s option ensures server is done 25 | 26 | ut_launch -new 1 lmake new_src # can build target once server is steady 27 | -------------------------------------------------------------------------------- /unit_tests/dyn_py.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | 10 | import lmake 11 | from lmake.rules import Rule,PyRule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'ref' 16 | ) 17 | 18 | class Hello(Rule) : 19 | target = 'hello.py' 20 | cmd = 'echo hello=$(cat ref)' 21 | 22 | class Test(PyRule) : 23 | target = 'test' 24 | stderr_ok = True 25 | def cmd(): 26 | import hello 27 | print(open('hello.py').read(),file=sys.stderr) 28 | print(hello.hello,file=sys.stderr) 29 | print(hello.hello) 30 | 31 | class Chk(Rule) : 32 | target = 'chk' 33 | deps = { 34 | 'TEST' : 'test' 35 | , 'REF' : 'ref' 36 | } 37 | cmd = 'diff {REF} {TEST}' 38 | 39 | else : 40 | 41 | import time 42 | 43 | import ut 44 | 45 | print('1',file=open('ref','w')) 46 | ut.lmake( 'chk' , done=3 , may_rerun=1 , new=2 ) # python reads Lmakefile.py to display backtrace 47 | ut.lmake( 'chk' , done=0 ) 48 | 49 | time.sleep(1) # python .pyc validation is only sensitive to seconds ! so to ensure .py are seen as different, we must wait 1s. 50 | print('2',file=open('ref','w')) 51 | ut.lmake( 'chk' , done=2 , changed=1 , steady=1 ) 52 | -------------------------------------------------------------------------------- /unit_tests/dyn_resources.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | from lmake.rules import Rule 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'cpu' 15 | ) 16 | 17 | def get_cpu() : 18 | return open('cpu').read().strip() 19 | 20 | class Dut(Rule) : 21 | targets = { 'TGT':r'{:.*}' } 22 | resources = { 23 | 'cpu' : get_cpu 24 | } 25 | cmd = ''' 26 | [ {TGT} = ko ] && exit 1 27 | [ {TGT} = read ] && cat cpu 28 | >{TGT} 29 | ''' 30 | 31 | else : 32 | 33 | import ut 34 | 35 | print(1,file=open('cpu','w')) 36 | ut.lmake( 'ok' , 'read' , 'ko' , new=1 , done=2 , failed=1 , rc=1 ) 37 | print(2,file=open('cpu','w')) 38 | ut.lmake( 'ok' , 'read' , 'ko' , changed=1 , steady=1 , failed=1 , rc=1 ) # check ok is not remade, but read is remade because it read cpu in job 39 | -------------------------------------------------------------------------------- /unit_tests/env.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.config.backends.local.environ = { 'DUT':'dut' } 12 | 13 | lmake.manifest = ('Lmakefile.py',) 14 | 15 | class Dut(Rule) : 16 | target = 'dut.ok' 17 | environ = { 'DUT':... } 18 | cmd = ''' 19 | [ "$DUT" = dut ] || echo bad '$DUT :' "$DUT != dut" >&2 20 | ''' 21 | 22 | else : 23 | 24 | import ut 25 | 26 | ut.lmake( 'dut.ok' , done=1 ) 27 | -------------------------------------------------------------------------------- /unit_tests/err.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Bad(Rule) : 14 | target = 'bad' 15 | cmd = 'exit 1' 16 | 17 | class Double(Rule) : 18 | targets = { 19 | 'DOUBLE1' : 'double1' 20 | , 'DOUBLE2' : 'double2' 21 | } 22 | deps = { 'BAD' : 'bad' } 23 | cmd = 'cat bad >{DOUBLE1} ; cp {DOUBLE1} {DOUBLE2}' 24 | 25 | class Dut(Rule) : 26 | target = 'dut' 27 | cmd = 'cat double1 double2' 28 | 29 | else : 30 | 31 | import ut 32 | 33 | ut.lmake( 'bad' , failed=1 , rc=1 ) 34 | ut.lmake( 'dut' , dep_error=1 , rc=1 ) # check Double is not run, although dut depends both on double1 and double2 35 | -------------------------------------------------------------------------------- /unit_tests/escape.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , '{hello}{}' 14 | , '{world}{}' 15 | ) 16 | 17 | class Cat(Rule) : 18 | stems = { 19 | 'File1' : r'.*' 20 | , 'File2' : r'.*' 21 | } 22 | deps = { 23 | 'FIRST' : '{File1}{{}}' 24 | , 'SECOND' : '{File2}{{}}' 25 | } 26 | 27 | class CatSh(Cat) : 28 | target = '{File1}{{}}{File2}_sh' 29 | cmd = 'cat {FIRST} {SECOND}' 30 | 31 | class CatPy(Cat,PyRule) : 32 | target = '{File1}{{}}{File2}_py' 33 | def cmd() : 34 | print(open(FIRST ).read(),end='') 35 | print(open(SECOND).read(),end='') 36 | 37 | else : 38 | 39 | import ut 40 | 41 | print('hello',file=open('{hello}{}','w')) 42 | print('world',file=open('{world}{}','w')) 43 | 44 | ut.lmake( '{hello}{}{world}_sh' , '{hello}{}{world}_py' , done=2 , new=2 ) # check targets are out of date 45 | -------------------------------------------------------------------------------- /unit_tests/forget.py: -------------------------------------------------------------------------------- 1 | 2 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | # Copyright (c) 2023-2025 Doliam 4 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | 7 | if __name__!='__main__' : 8 | 9 | import lmake 10 | from lmake.rules import Rule 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'step.py' 15 | , 'dep' 16 | ) 17 | 18 | from step import step 19 | 20 | class Dut(Rule) : 21 | target = 'dut' 22 | deps = { 'DEP':'dep' } 23 | resources = { 'cpu':step } 24 | cmd = 'cat no_file 2>/dev/null ; cat {DEP} ; [ {cpu} != 2 ]' 25 | 26 | else : 27 | 28 | import subprocess as sp 29 | 30 | import ut 31 | 32 | open('dep','w') 33 | 34 | print('step=1',file=open('step.py','w')) 35 | 36 | ut.lmake( 'dut' , done=1 , new=1 ) 37 | 38 | sp.run(('lforget','-d', 'dut'),check=True) ; ut.lmake( 'dut' , steady=1 ) 39 | sp.run(('lforget','-d','-J','dut'),check=True) ; ut.lmake( 'dut' , steady=1 ) 40 | sp.run(('lforget','-r' ),check=True) ; ut.lmake( 'dut' ) 41 | 42 | print('step=2',file=open('step.py','w')) # dont run lmake before lforget to ensure lforget forces a rule refresh 43 | sp.run(('lforget','-r'),check=True) ; ut.lmake( 'dut' , failed=1 , rc=1 ) # force taking into account resources modif, even for ok jobs 44 | print('step=3',file=open('step.py','w')) ; ut.lmake( 'dut' , steady=1 ) # resources modif rerun ko jobs 45 | 46 | -------------------------------------------------------------------------------- /unit_tests/generic_sources.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,SourceRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class XSrc(SourceRule) : 14 | target = r'{File:.*}.src' 15 | 16 | class Cpy(Rule) : 17 | target = r'{File:.*}.cpy' 18 | dep = '{File}' 19 | cmd = 'cat' 20 | 21 | class Test(Rule) : 22 | target = 'test' 23 | side_targets = { 'SUB' : ( r'{*:.*}.src' , 'source_ok','incremental' ) } 24 | cmd = ''' 25 | echo sub > sub.src 26 | cat sub.src.cpy 27 | ''' 28 | 29 | else : 30 | 31 | import ut 32 | 33 | ut.lmake( 'test' , may_rerun=1 , done=2 ) 34 | -------------------------------------------------------------------------------- /unit_tests/hard_link.py: -------------------------------------------------------------------------------- 1 | 2 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 3 | # Copyright (c) 2023-2025 Doliam 4 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 5 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 6 | 7 | if __name__!='__main__' : 8 | 9 | import lmake 10 | from lmake.rules import Rule 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'src' 15 | ) 16 | 17 | class Dep(Rule) : 18 | targets = { 19 | 'TGT1' : ( 'a' , 'incremental' ) 20 | , 'TGT2' : ( 'b' , 'incremental' ) 21 | } 22 | dep = 'src' 23 | cmd = ''' 24 | cat > {TGT1} 25 | [ "$(cat {TGT2} 2>/dev/null)" == "$(cat {TGT1})" ] || ln -f {TGT1} {TGT2} 26 | ''' 27 | 28 | class Dut(Rule) : 29 | targets = { 30 | 'TGT1' : ( 'c' , 'incremental' ) 31 | , 'TGT2' : ( 'd' , 'incremental' ) 32 | } 33 | deps = { 34 | 'DEP1' : 'a' 35 | , 'DEP2' : 'b' 36 | } 37 | cmd = ''' 38 | [ "$(cat {TGT1} 2>/dev/null)" == "$(cat {DEP1})" ] || ln -f {DEP1} {TGT1} 39 | [ "$(cat {TGT2} 2>/dev/null)" == "$(cat {DEP2})" ] || ln -f {DEP2} {TGT2} 40 | ''' 41 | 42 | else : 43 | 44 | import os 45 | import os.path as osp 46 | 47 | import ut 48 | 49 | print('1',file=open('src','w')) 50 | 51 | ut.lmake( 'c' , 'd' , done=2 , new=1 ) 52 | assert os.stat('c').st_nlink==4 53 | 54 | print('2',file=open('src','w')) 55 | 56 | ut.lmake( 'a' , 'b' , done=1 , changed=1 ) 57 | assert os.stat('a').st_nlink==2 58 | assert os.stat('c').st_nlink==2 59 | 60 | ut.lmake( 'c' , 'd' , done=1 ) 61 | assert os.stat('a').st_nlink==4 62 | -------------------------------------------------------------------------------- /unit_tests/hard_lnks.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'src' 14 | ) 15 | 16 | class Cpy(Rule) : 17 | target = 'cpy' 18 | dep = 'src' 19 | cmd = 'cat' 20 | 21 | class Lnk(Rule) : 22 | targets = { 'DST':'lnk' } 23 | deps = { 'SRC':'cpy' } 24 | cmd = 'ln {SRC} {DST}' 25 | 26 | class Chk(Rule) : 27 | target = 'chk' 28 | deps = { 29 | 'DUT' : 'lnk' 30 | , 'REF' : 'src' 31 | } 32 | cmd = 'diff {DUT} {REF} >&2' 33 | 34 | else : 35 | 36 | import ut 37 | 38 | print('hello',file=open('src','w')) 39 | 40 | ut.lmake( 'chk' , done=3 , new=1 ) # check targets are out of date 41 | ut.lmake( 'chk' , done=0 , new=0 ) # check targets are up to date 42 | 43 | print('hello2',file=open('src','w')) 44 | 45 | ut.lmake( 'chk' , done=2 , steady=1 , changed=1 ) # check targets are out of date 46 | ut.lmake( 'chk' , done=0 ) # check targets are up to date 47 | -------------------------------------------------------------------------------- /unit_tests/hide.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'hello' 14 | ) 15 | 16 | class Auto(Rule) : 17 | target = r'auto{Digit:\d}' 18 | cmd = "echo '#auto'{Digit}" 19 | 20 | class Hide(Rule) : 21 | target = r'{File:.*}.hide' 22 | stderr_ok = True 23 | cmd = 'cat {File} || :' 24 | 25 | class Cat(Rule) : 26 | prio = 1 27 | stems = { 28 | 'File1' : r'.*' 29 | , 'File2' : r'.*' 30 | } 31 | target = '{File1}+{File2}' 32 | deps = { 33 | 'FIRST' : '{File1}' 34 | , 'SECOND' : '{File2}' 35 | } 36 | cmd = 'cat {FIRST} {SECOND}' 37 | 38 | else : 39 | 40 | import ut 41 | 42 | print('hello',file=open('hello','w')) 43 | 44 | ut.lmake( 'hello+auto1.hide' , done=3 , may_rerun=1 , new=1 ) # check target is out of date 45 | ut.lmake( 'hello+auto1.hide' , done=0 , new=0 ) # check target is up to date 46 | -------------------------------------------------------------------------------- /unit_tests/home.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import os 9 | 10 | import lmake 11 | from lmake.rules import Rule,HomelessRule,PyRule 12 | 13 | lmake.manifest = ('Lmakefile.py',) 14 | 15 | class Home(Rule) : 16 | target = 'home' 17 | environ = { 'REPO_ROOT' : '$REPO_ROOT' } 18 | cmd = '[ $HOME = $REPO_ROOT ]' 19 | 20 | class Homeless1(HomelessRule) : 21 | target = 'homeless1' 22 | cmd = '[ $HOME = $TMPDIR ]' 23 | 24 | class Homeless2(HomelessRule) : 25 | target = 'homeless2' 26 | cmd = '[ {0}$HOME = {0}$TMPDIR ]' # force python execution of f-string 27 | 28 | class Homeless3(HomelessRule,PyRule) : 29 | target = 'homeless3' 30 | def cmd() : 31 | assert os.environ['HOME'] == os.environ['TMPDIR'] 32 | 33 | else : 34 | 35 | import ut 36 | 37 | ut.lmake( 'home','homeless1','homeless2','homeless3' , done=4 ) 38 | -------------------------------------------------------------------------------- /unit_tests/hot.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | lmake.config.disk_date_precision = 2 14 | 15 | class Dep(Rule) : 16 | target = 'dep' 17 | cmd = '' 18 | 19 | class Dut(Rule) : 20 | target = r'dut.{S:\d+}' 21 | cmd = 'sleep {S} ; cat dep' 22 | 23 | else : 24 | 25 | import os 26 | import os.path as osp 27 | 28 | import ut 29 | 30 | ut.lmake( 'dep' , 'dut.1' , 'dut.3' , done=3 , rerun=1 ) # check dut.1 is rerun despite dep being read 1s after available, but not dut.3 31 | -------------------------------------------------------------------------------- /unit_tests/ignore_err.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'hello' 14 | , 'world' 15 | ) 16 | 17 | class Bad(Rule) : 18 | target = 'bad' 19 | cmd = 'exit 1' 20 | 21 | class Cat(Rule) : 22 | stems = { 23 | 'File1' : r'.*' 24 | , 'File2' : r'.*' 25 | } 26 | target = '{File1}+{File2}' 27 | deps = { 28 | 'FIRST' : '{File1}' 29 | , 'SECOND' : ( '{File2}' , 'IgnoreError' ) 30 | } 31 | cmd = 'cat {FIRST} {SECOND}' 32 | 33 | else : 34 | 35 | import ut 36 | 37 | print('hello',file=open('hello','w')) 38 | print('world',file=open('world','w')) 39 | 40 | ut.lmake( 'hello+bad' , new=1 , failed=1 , done=1 ) # check this is ok 41 | ut.lmake( 'bad+world' , new=1 , rc=1 ) # check this is not 42 | -------------------------------------------------------------------------------- /unit_tests/import2.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Doliam 2 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 3 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 4 | 5 | if __name__!='__main__' : 6 | 7 | import lmake 8 | from lmake.rules import Rule,PyRule 9 | 10 | lmake.manifest = ( 11 | 'Lmakefile.py' 12 | , 'a/b/c.py' 13 | ) 14 | 15 | class Dut(PyRule) : 16 | target = 'dut' 17 | environ = { 'PYTHONPATH' : 'a' } # check namespaces with several branches : based on repo root (automatic) and on a 18 | def cmd() : 19 | import b.c # check namespace based import from a 20 | import a.b.c # check namespace based import from repo root 21 | 22 | else : 23 | 24 | import os 25 | 26 | import ut 27 | 28 | os.makedirs('a/b',exist_ok=True) 29 | os.makedirs('b' ,exist_ok=True) # this dir is searched, but c.py is not there 30 | open('a/b/c.py','w') 31 | 32 | ut.lmake( 'dut' , done=1 , new=1 ) 33 | -------------------------------------------------------------------------------- /unit_tests/incremental.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import os 9 | 10 | import lmake 11 | from lmake.rules import PyRule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'dep' 16 | ) 17 | 18 | class Inc(PyRule) : 19 | targets = { 20 | 'MANIFEST' : ( 'manifest' , 'incremental' ) # we need old value to compute old/new files 21 | , 'TGT' : ( 'dut.{Sfx*:.*}' , 'incremental' ) 22 | } 23 | deps = { 'DEP' : 'dep' } 24 | def cmd() : 25 | old_targets = set() 26 | new_targets = set() 27 | # 28 | if True : new_txt = open(DEP ).read() 29 | try : old_txt = open(MANIFEST).read() 30 | except FileNotFoundError : old_txt = '' 31 | for f in new_txt.strip().split() : new_targets.add(TGT(f)) 32 | for f in old_txt.strip().split() : old_targets.add(TGT(f)) 33 | # 34 | print(old_targets,new_targets) 35 | open(MANIFEST,'w').write(new_txt) 36 | for f in new_targets-old_targets : print(file=open(f,'w')) # create new targets 37 | for f in old_targets-new_targets : os.unlink(f) # remove old ones 38 | 39 | else : 40 | 41 | import ut 42 | 43 | print( 'a' , 'b' , file=open('dep','w') ) 44 | ut.lmake( 'dut.b' , done=1 , new=1 ) 45 | 46 | print( 'a' , 'c' , file=open('dep','w') ) 47 | ut.lmake( 'dut.a' , done=1 , changed=1 ) 48 | ut.lmake( 'dut.b' , rc=1 ) 49 | ut.lmake( 'dut.c' ) 50 | -------------------------------------------------------------------------------- /unit_tests/infinite.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | from lmake.rules import Rule 11 | 12 | lmake.config.max_dep_depth = 10 13 | lmake.config.path_max = 20 14 | 15 | lmake.manifest = ('Lmakefile.py',) 16 | 17 | class Inf1(Rule) : 18 | target = '{X:.*}x1' 19 | dep = 'a{X}x1' 20 | cmd = '' 21 | 22 | class Inf3(Rule) : 23 | target = '{X:.*}x3' 24 | dep = 'aaa{X}x3' 25 | cmd = '' 26 | 27 | class Impossible(Rule) : 28 | prio = 1 29 | target = '{X:.*}x' 30 | dep = 'b' 31 | cmd = '' 32 | 33 | class LowPrio(Rule) : 34 | prio = -1 35 | target = '{X:.*}x' 36 | dep = '' 37 | cmd = '' 38 | 39 | else : 40 | 41 | import ut 42 | 43 | ut.lmake( 'x1' , failed=1 , rc=1 ) # limit is max_dep_depth 44 | ut.lmake( 'x3' , failed=1 , rc=1 ) # limit is path_max 45 | -------------------------------------------------------------------------------- /unit_tests/jemalloc.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'hello' 14 | , 'world' 15 | ) 16 | 17 | class Cpy(Rule) : 18 | stems = { 'File' : r'.*' } 19 | autodep = 'ld_preload_jemalloc' 20 | environ = { 'LD_PRELOAD':'libjemalloc.so' } 21 | 22 | class Auto(Rule) : 23 | target = r'auto{D:\d+}' 24 | cmd = 'echo {D}' 25 | 26 | class CpySh(Cpy) : 27 | target = '{File}_sh' 28 | cmd = 'cat {File}' 29 | 30 | class CpyPy(Cpy,PyRule) : 31 | target = '{File}_py' 32 | def cmd() : 33 | print(open(File).read(),end='') 34 | 35 | else : 36 | 37 | import os 38 | import subprocess as sp 39 | import sys 40 | 41 | sav = os.environ.get('LD_PRELOAD') 42 | os.environ['LD_PRELOAD'] = 'libjemalloc.so' 43 | has_jemalloc = not sp.run(('/usr/bin/echo',),check=True,stderr=sp.PIPE).stderr 44 | if sav is None : del os.environ['LD_PRELOAD'] 45 | else : os.environ['LD_PRELOAD'] = sav 46 | 47 | if not has_jemalloc : 48 | print('jemalloc not available',file=open('skipped','w')) 49 | exit() 50 | 51 | import ut 52 | 53 | print('hello',file=open('hello','w')) 54 | print('world',file=open('world','w')) 55 | 56 | ut.lmake( 'auto1_sh' , 'auto2_py' , may_rerun=2 , done=4 , new=1 ) # check deps are acquired 57 | -------------------------------------------------------------------------------- /unit_tests/jobs.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | n_jobs = 4 7 | 8 | if __name__!='__main__' : 9 | 10 | import os 11 | 12 | import lmake 13 | from lmake.rules import PyRule 14 | 15 | lmake.manifest = ( 16 | 'Lmakefile.py' 17 | , 'trig' 18 | ) 19 | 20 | lmake.config.backends.local.cpu = n_jobs*2 21 | lmake.config.backends.local.mem = f'{n_jobs*2}M' 22 | 23 | class GenFile(PyRule) : 24 | target = r'file_{:\d+}' 25 | deps = {'TRIG':'trig'} 26 | resources = {'mem':'1M'} 27 | environ = {'SMALL_ID':'$SMALL_ID' } 28 | def cmd() : 29 | lmake.depend(TRIG,read=True) 30 | assert int(os.environ['SMALL_ID'])<=n_jobs , f"small id is {os.environ['SMALL_ID']} > {n_jobs}" 31 | 32 | class Trig(PyRule) : 33 | target = r'out_{N:\d+}' 34 | resources = {'mem':'1M'} 35 | def cmd() : 36 | lmake.depend(*(f'file_{x}' for x in range(int(N)))) 37 | 38 | else : 39 | 40 | import ut 41 | 42 | n = n_jobs*2+10 43 | print(1,file=open('trig','w')) ; ut.lmake( '-j' , str(n_jobs) , f'out_{n}' , new=1 , may_rerun=1 , done=n , was_done=1 ) 44 | print(2,file=open('trig','w')) ; ut.lmake( f'out_{n}' , new=1 , failed=... , steady=... , changed=1 , rc=1 ) # python reads Lmakefile.py to display backtrace 45 | -------------------------------------------------------------------------------- /unit_tests/kill.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Dut(Rule) : 14 | kill_sigs = (2,) # SIGKILL (9) is automatically added when list is exhausted 15 | targets = { 'TGT' : ( r'dut.{*:\d}' , 'incremental' ) } 16 | cmd = ''' 17 | trap 'echo killed > {TGT(2)} ; sleep 2 ; echo killed >{TGT(3)}' 2 18 | cat dep > {TGT(1)} 19 | lcheck_deps --sync # signal 2 may not be received by following sleep because of a race inside bash if fired just at the beginning of sleep 20 | sleep 3 21 | ''' 22 | 23 | class Dep(Rule) : 24 | target = 'dep' 25 | cmd = '' 26 | 27 | else : 28 | 29 | import os 30 | import os.path as osp 31 | 32 | import ut 33 | 34 | ut.lmake( 'dut.1' , rerun=1 , done=2 ) 35 | 36 | assert osp.exists('dut.2') # created when job is killed because of new dep 37 | assert not osp.exists('dut.3') # should be kill -9'ed before generating 2nd message 38 | -------------------------------------------------------------------------------- /unit_tests/ld_library_path.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | from lmake.rules import Rule 11 | 12 | lmake.manifest = ('Lmakefile.py',) 13 | 14 | class Dut(Rule) : 15 | target = r'dut.{Autodep:\w+}' 16 | environ = { 'LD_LIBRARY_PATH':'a_dir' } 17 | autodep = '{Autodep}' 18 | stderr_ok = True 19 | cmd = 'hostname ; hostname' 20 | 21 | else : 22 | 23 | import subprocess as sp 24 | 25 | import ut 26 | 27 | for ad in lmake.autodeps : 28 | ut.lmake( f'dut.{ad}' , done=1 ) 29 | x = sp.run(('lshow','-dv',f'dut.{ad}'),stdout=sp.PIPE,universal_newlines=True).stdout 30 | assert 'a_dir/libc.so' in x,f'no libc.so in :\n{x}' 31 | -------------------------------------------------------------------------------- /unit_tests/link.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | date_prec = 1 7 | 8 | if __name__!='__main__' : 9 | 10 | import lmake 11 | from lmake.rules import Rule 12 | 13 | lmake.config.disk_date_precision = date_prec 14 | 15 | lmake.manifest = ( 16 | 'Lmakefile.py' 17 | , 'a/src' 18 | ) 19 | 20 | class MkB(Rule) : 21 | targets = { 'LNK' : 'b' } 22 | cmd = 'ln -s a {LNK}' 23 | 24 | class Test(Rule) : 25 | target = 'test' 26 | cmd = 'cat b/src' 27 | 28 | else : 29 | 30 | import os 31 | import time 32 | 33 | import ut 34 | 35 | os.makedirs('a',exist_ok=True) 36 | print('src',file=open('a/src','w')) 37 | time.sleep(date_prec) # ensure source a/src is old enough 38 | 39 | ut.lmake( 'test' , may_rerun=1 , done=2 , new=1 ) 40 | -------------------------------------------------------------------------------- /unit_tests/live_out.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Dut(Rule) : 14 | targets = { 'DUT':r'dut.{N:\d+}' } 15 | cmd = ''' 16 | sleep 1 17 | echo before 18 | sleep 1 19 | echo after 20 | sleep 1 21 | > {DUT} 22 | ''' 23 | 24 | else : 25 | 26 | import ut 27 | 28 | cnt = ut.lmake( '-o' , 'dut.1' , 'dut.2' , done=2 , **{'continue':...} ) 29 | assert cnt['continue'] in (1,2) 30 | -------------------------------------------------------------------------------- /unit_tests/mandelbrot.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cesar-douady/open-lmake/86dcc9fbe08129005d0f4416cda69cc4c5fb653f/unit_tests/mandelbrot.zip -------------------------------------------------------------------------------- /unit_tests/manual_target.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | 10 | import lmake 11 | from lmake import multi_strip 12 | from lmake.rules import Rule,PyRule 13 | 14 | lmake.manifest = ( 15 | 'Lmakefile.py' 16 | , 'src' 17 | ) 18 | 19 | class Star(Rule) : 20 | targets = { 'DST' : r'a{*:\d}' } 21 | dep = 'src' 22 | cmd = multi_strip(''' 23 | echo good > a1 24 | [ -e a2 ] 25 | ln a1 a2 26 | rm a1 27 | ''') 28 | 29 | class Cpy(PyRule) : 30 | target = 'cpy' 31 | dep = 'a2' 32 | def cmd() : 33 | assert sys.stdin.read().strip()=='good' 34 | 35 | else : 36 | 37 | import ut 38 | 39 | print(1,file=open('src','w')) 40 | 41 | open('a2','w').write('bad1') 42 | 43 | ut.lmake( 'cpy' , quarantined=1 , done=2 , new=1 ) 44 | 45 | open('a2','w').write('bad1') 46 | 47 | ut.lmake( 'cpy' ) 48 | 49 | print(2,file=open('src','w')) 50 | 51 | ut.lmake( 'cpy' , changed=1 , steady=1 ) 52 | -------------------------------------------------------------------------------- /unit_tests/misc1.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | from step import step 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'step.py' 16 | ) 17 | 18 | class Test(Rule) : 19 | target = 'test' 20 | stderr_ok = True 21 | if step==1 : cmd = "cat a ; echo >a" 22 | else : cmd = "cat a ; : " 23 | 24 | else : 25 | 26 | import os 27 | 28 | import ut 29 | 30 | os.environ['LMAKE_ARGS'] = 'test' 31 | 32 | print('step=1',file=open('step.py','w')) 33 | ut.lmake( failed=1 , new=0 , rc=1 ) # unexpected write to a 34 | 35 | os.unlink('a') 36 | print('step=2',file=open('step.py','w')) 37 | ut.lmake( steady=1 , new=0 , rc=0 ) # fixed 38 | -------------------------------------------------------------------------------- /unit_tests/misc10.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | import time 10 | 11 | import lmake 12 | from lmake.rules import Rule 13 | 14 | lmake.manifest = ('Lmakefile.py',) 15 | 16 | class BaseRule(Rule) : 17 | start_delay = 0 18 | 19 | class Good(BaseRule) : 20 | target = 'good' 21 | cmd = 'echo good_content' 22 | 23 | class Bad(BaseRule) : 24 | targets = { 'Bad' : r'bad1{*:}' } 25 | cmd = 'sleep 3' # dont produce bad 26 | 27 | class Ptr(BaseRule) : 28 | target = 'ptr' 29 | cmd = ' echo good ; sleep 2 ' 30 | 31 | class Dut(BaseRule) : 32 | target = 'dut' 33 | stderr_ok = True 34 | cmd = ''' 35 | deps="$( cat ptr 2>/dev/null || echo bad1 bad2 )" 36 | ldepend $deps # makes deps required 37 | echo $deps $(cat $deps) >&2 38 | echo $(cat $deps) 39 | sleep 2 40 | ''' 41 | 42 | class Side(BaseRule) : 43 | target = 'side' 44 | cmd = ' sleep 3 ; cat ptr good ' 45 | 46 | class chk(BaseRule) : 47 | target = 'chk' 48 | deps = { 'DUT':'dut' , 'SIDE':'side' } 49 | cmd = '[ "$(cat {DUT})" = good_content ]' 50 | 51 | else : 52 | 53 | import ut 54 | 55 | ut.lmake( 'chk' , done=5 , may_rerun=2 , steady=1 ) 56 | -------------------------------------------------------------------------------- /unit_tests/misc11.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | import time 10 | 11 | import lmake 12 | from lmake.rules import Rule 13 | 14 | lmake.manifest = ( 15 | 'Lmakefile.py' 16 | , 'src1' 17 | , 'src2' 18 | ) 19 | 20 | class Dep(Rule) : 21 | target = r'dep{D:\d+}' 22 | dep = 'src{D }' 23 | cmd = 'sleep 2 ; echo dep_content {D}' 24 | 25 | class Dut(Rule) : 26 | target = 'dut' 27 | cmd = 'cat dep1 dep2' 28 | 29 | class Ref(Rule) : 30 | target = 'dut.ref' 31 | cmd = 'echo dep_content 1 ; echo dep_content 2' 32 | 33 | class Test(Rule) : 34 | target = r'test{D:\d+}' 35 | deps = { 'REF' : 'dut.ref' } 36 | cmd = 'sleep 1 ; diff {REF} dut' 37 | 38 | else : 39 | 40 | import os 41 | 42 | import ut 43 | 44 | print('src1 v1',file=open('src1','w')) 45 | print('src2 v1',file=open('src2','w')) 46 | 47 | ut.lmake( 'test1' , done=5 , may_rerun=2 , new=2 ) # dut is made 48 | 49 | os.unlink('dep1') 50 | os.unlink('dut') 51 | print('src2 v2',file=open('src2','w')) 52 | 53 | ut.lmake( 'test1' , 'test2' , changed=1 , may_rerun=1 , done=1 , steady=3 ) # dut is not necessary on disk for test1 as dep2 will be steady and test1 will not run, ... 54 | # # ... but 1s later, while it is waiting for dep2, it is necessary for test2 55 | -------------------------------------------------------------------------------- /unit_tests/misc12.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | import time 10 | 11 | import lmake 12 | from lmake.rules import Rule 13 | 14 | lmake.manifest = ('Lmakefile.py',) 15 | 16 | class DepErr(Rule) : 17 | target = 'dep_err' 18 | cmd = 'echo bad ; exit 1' 19 | 20 | class Dut(Rule) : 21 | target = 'dut' 22 | cmd = 'ldepend -e dep_err ; cat dep_err' 23 | 24 | else : 25 | 26 | import ut 27 | 28 | ut.lmake('dep_err',failed=1,rc=1) 29 | 30 | print('manual',file=open('dep_err','w')) 31 | 32 | ut.lmake('dut',quarantined=1,may_rerun=1,failed=1,done=1,rc=0) 33 | -------------------------------------------------------------------------------- /unit_tests/misc13.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | import time 10 | 11 | import lmake 12 | from lmake.rules import Rule 13 | 14 | lmake.manifest = ( 15 | 'Lmakefile.py' 16 | , 'step.py' 17 | ) 18 | 19 | from step import step 20 | 21 | class Hdep(Rule) : 22 | target = r'hdep' 23 | cmd = 'echo {step}' 24 | 25 | class Sdep(Rule) : 26 | target = r'sdep' 27 | cmd = 'echo sdep' 28 | 29 | class Dut(Rule) : 30 | target = 'dut' 31 | deps = { 'SDEP' : 'sdep' } 32 | cmd = '[ $(cat hdep) = 2 ] && cat sdep' 33 | 34 | else : 35 | 36 | import os 37 | 38 | import ut 39 | 40 | print('step=1',file=open('step.py','w')) 41 | ut.lmake( 'dut' , may_rerun=1 , done=2 , failed=1 , rc=1 ) 42 | 43 | print('step=2',file=open('step.py','w')) 44 | os.unlink('sdep') 45 | ut.lmake( 'dut' , done=2 , steady=1 ) # check sdep is not forgetten due to execution w/o hdep 46 | -------------------------------------------------------------------------------- /unit_tests/misc14.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | import time 10 | 11 | import lmake 12 | from lmake.rules import Rule,PyRule 13 | 14 | lmake.manifest = ('Lmakefile.py',) 15 | 16 | class DutSh(Rule) : 17 | targets = { 'DUT' : r'dut_sh{N*:\d}' } 18 | cmd = ''' 19 | [ -f dut_sh1 ] && exit 1 ; echo 1 >dut_sh1 20 | [ -f dut_sh2 ] && exit 1 ; echo 2 >dut_sh2 21 | ''' 22 | 23 | class DutPy(PyRule) : 24 | targets = { 'DUT' : r'dut_py{N*:\d}' } 25 | def cmd() : 26 | print(1,file=open('dut_py1','x')) 27 | print(2,file=open('dut_py2','x')) 28 | 29 | else : 30 | 31 | import ut 32 | 33 | print('manual',file=open('dut_sh2','w')) 34 | print('manual',file=open('dut_py2','w')) 35 | ut.lmake( 'dut_sh1' , rerun=1 , done=1 ) # check dut_sh2 is quarantined and job rerun 36 | ut.lmake( 'dut_py1' , rerun=1 , done=1 , new=1 ) # check dut_py2 is quarantined and job rerun 37 | -------------------------------------------------------------------------------- /unit_tests/misc15.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | from lmake.rules import PyRule 11 | 12 | lmake.manifest = ('Lmakefile.py',) 13 | 14 | class Dut(PyRule) : 15 | targets = { 'DUT' : r'dut/{*:.*}' } 16 | def cmd() : 17 | import os 18 | import subprocess as sp 19 | open(DUT('before'),'w') 20 | sp.run(('hostname',)) 21 | open(DUT('after'),'w') # ensure access recording still works after having called sp.run 22 | 23 | else : 24 | 25 | import ut 26 | 27 | ut.lmake('dut/before','dut/after',done=1) 28 | -------------------------------------------------------------------------------- /unit_tests/misc2.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import PyRule 10 | 11 | from step import step 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'step.py' 16 | , 'no_dep.1' 17 | , 'no_dep.2' 18 | ) 19 | 20 | class Test(PyRule) : 21 | targets = { 'DST' : r'{File:.*}.test' } 22 | if step==1 : 23 | side_targets = { 24 | 'UNIQ' : 'uniq' 25 | , 'SIDE' : r'side.{*:[12]}' 26 | } 27 | side_deps = { 'NO_DEP' : ( r'no_dep.{*:[12]}' , 'Ignore' ) } 28 | stderr_ok = True 29 | def cmd() : 30 | open('no_dep.1') 31 | open('no_dep.2') 32 | if step==1 : 33 | open('uniq' ,'w') 34 | open('side.1','w') 35 | open('side.2','w') 36 | open(DST,'w') 37 | 38 | else : 39 | 40 | import os 41 | 42 | import ut 43 | 44 | print(file=open('no_dep.1','w')) 45 | print(file=open('no_dep.2','w')) 46 | 47 | print('step=1',file=open('step.py','w')) 48 | ut.lmake( 'foo.test' , done=1 ) 49 | os.unlink('foo.test') 50 | ut.lmake( 'foo.test' , steady=1 ) 51 | 52 | print('step=2',file=open('step.py','w')) 53 | ut.lmake( 'foo.test' , steady=1 , new=2 ) 54 | 55 | print('v2',file=open('no_dep.1','w')) 56 | ut.lmake( 'foo.test' , steady=1 , changed=1 ) 57 | -------------------------------------------------------------------------------- /unit_tests/misc3.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | from lmake.rules import PyRule 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'step.py' 15 | ) 16 | 17 | from step import step 18 | 19 | class X(PyRule) : 20 | targets = { 'X' : r'{Key:[12]}{*:}' } 21 | def cmd() : 22 | if int(Key)!=step : raise ValueError('bad Key') 23 | open(Key,'w') 24 | 25 | class Test(PyRule) : 26 | target = 'test' 27 | def cmd() : 28 | open(f'{step}') 29 | 30 | else : 31 | 32 | import ut 33 | 34 | print('step=1',file=open('step.py','w')) 35 | ut.lmake('test',new=1,may_rerun=1,done=2) 36 | 37 | print('step=2',file=open('step.py','w')) 38 | ut.lmake('test',failed=1,may_rerun=1,done=1,steady=1) 39 | -------------------------------------------------------------------------------- /unit_tests/misc4.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | from lmake.rules import Rule,PyRule 11 | 12 | lmake.manifest = ('Lmakefile.py',) 13 | 14 | class Cat(Rule) : 15 | targets = { 'CAT' : 'bin/cat' } 16 | cmd = 'ln -s /usr/bin/cat {CAT}' 17 | 18 | class Reg(Rule) : 19 | target = r'x{:\d}' 20 | cmd = '' 21 | 22 | class Dut1(Rule) : 23 | target = 'dut1' 24 | deps = { 'X1' : 'x1' } 25 | cmd = 'readlink x1 ; readlink x2' 26 | 27 | class Dut2(Rule) : 28 | target = 'dut2' 29 | cmd = 'bin/cat x3' 30 | 31 | class Dut3(PyRule) : 32 | target = 'dut3' 33 | def cmd() : 34 | import os 35 | os.lstat('bin/cat') 36 | 37 | else : 38 | 39 | import ut 40 | 41 | ut.lmake('dut1',may_rerun=1,done=2,was_failed=1,rc=1) 42 | ut.lmake('dut2',may_rerun=2,done=3 ) 43 | ut.lmake('dut3',may_rerun=0,done=1 ) 44 | -------------------------------------------------------------------------------- /unit_tests/misc6.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | from lmake.rules import Rule 11 | 12 | lmake.manifest = ('Lmakefile.py',) 13 | 14 | class PyEnv(Rule) : 15 | targets = { 'DUT' : 'dut' } 16 | stderr_ok = True 17 | max_submits = 1 18 | cmd = ''' 19 | stat {DUT}.tmp 20 | echo dut > {DUT}.tmp 21 | mv {DUT}.tmp {DUT} 22 | ''' 23 | 24 | else : 25 | 26 | import ut 27 | 28 | ut.lmake('dut',done=1) 29 | -------------------------------------------------------------------------------- /unit_tests/misc7.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | from lmake.rules import HomelessRule,PyRule 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'step.py' 15 | ) 16 | 17 | from step import step 18 | 19 | class Test(HomelessRule,PyRule) : 20 | targets = { 'OUT' : 'out' } 21 | def cmd() : 22 | import lmake 23 | if step==1 : lmake.target('side') 24 | else : lmake.target('side',incremental=True) 25 | open('side','w').close() 26 | print('toto') 27 | 28 | else : 29 | 30 | import os.path as osp 31 | 32 | import ut 33 | 34 | print('step=1',file=open('step.py','w')) 35 | ut.lmake('out',failed=1,rc=1) 36 | assert osp.exists('side') 37 | 38 | print('step=2',file=open('step.py','w')) 39 | ut.lmake('out',failed=1,rc=1) 40 | assert osp.exists('side') 41 | -------------------------------------------------------------------------------- /unit_tests/misc8.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.config.link_support = None # avoid dut1 being read as a link when running dut2 for the first time 12 | 13 | from step import step 14 | 15 | lmake.manifest = ( 16 | 'Lmakefile.py' 17 | , 'step.py' 18 | ) 19 | 20 | class Dep(Rule) : 21 | target = 'dep' 22 | cmd = 'echo {step}' 23 | 24 | class Dut1(Rule) : 25 | target = 'dut1' 26 | cmd = 'echo good' 27 | 28 | class Dut2(Rule) : 29 | target = 'dut2' 30 | deps = { 'DUT1' : 'dut1' } 31 | cmd = ''' 32 | if [ $(cat dep) = 1 ] 33 | then echo bad ; echo bad > dut1 34 | else cat dut1 35 | fi 36 | ''' 37 | 38 | class Chk(Rule) : 39 | target = 'chk' 40 | deps = { 'DUT' : 'dut2' } 41 | cmd = '[ $(cat {DUT}) = good ]' 42 | 43 | else : 44 | 45 | import os 46 | 47 | import ut 48 | 49 | print('step=1',file=open('step.py','w')) 50 | ut.lmake( 'dep' , done=1 ) 51 | 52 | print('step=2',file=open('step.py','w')) 53 | ut.lmake( 'chk' , may_rerun=1 , done=5 ) # check that dut1 is remade after first run of dut2 despite having been done before 54 | -------------------------------------------------------------------------------- /unit_tests/misc9.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Good(Rule) : 14 | target = r'good{D:\d+}' 15 | cmd = '' 16 | 17 | class Dut(Rule) : 18 | target = 'dut' 19 | cmd = ''' 20 | if cat good1 ; then ldepend good2 21 | else ldepend bad2 22 | fi 23 | ''' 24 | 25 | else : 26 | 27 | import ut 28 | 29 | ut.lmake( 'dut' , may_rerun=2 , done=2 , was_done=1 ) # check that errors past out-of-date does not prevent rerun 30 | -------------------------------------------------------------------------------- /unit_tests/mk_py1.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | REF=src1+{src2.version}.ref 9 | TARGET=src1+{src2.version}.ok 10 | ( cat src1 src2 ; echo '#version1' ) > $REF 11 | echo $REF >>Manifest 12 | 13 | ut_launch -done 6 -new 3 lmake $TARGET.cpy 14 | 15 | # update source 16 | echo x >> src2 17 | ut_launch -done 4 -failed 2 -changed 1 -rc 1 lmake $TARGET.cpy 18 | 19 | ( cat src1 src2 ; echo '#version1' ) > $REF 20 | ut_launch -done 2 -changed 1 -rc 0 -steady 0 lmake $TARGET.cpy 21 | 22 | # update rule cmd 23 | echo 'version = 2' >> Lmakefile.py 24 | ut_launch -done 4 -failed 2 -rc 1 lmake $TARGET 25 | 26 | ( cat src1 src2 ; echo '#version2' ) > $REF 27 | ut_launch -steady 0 -done 2 -changed 1 -rc 0 lmake $TARGET 28 | 29 | echo 'version = 3' >> Lmakefile.py 30 | ut_launch -done 4 -failed 2 -rc 1 lmake $TARGET 31 | 32 | ( cat src1 src2 ; echo '#version3' ) > $REF 33 | ut_launch -done 2 -changed 1 -rc 0 -steady 0 lmake $TARGET 34 | 35 | # update rules 36 | ut_launch -done 1 -rc 1 lmake $TARGET.cpyn 37 | 38 | echo 'add_rule()' >> Lmakefile.py 39 | ut_launch -done 2 -rc 0 lmake $TARGET.cpyn 40 | -------------------------------------------------------------------------------- /unit_tests/mk_py2.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | ut_launch -dangling 0 -done 2 -failed 0 -steady 0 -manual 0 -rc 0 lmake src3.hide 9 | 10 | touch src3 11 | rm src3.hide 12 | ut_launch -dangling 1 -done 1 -failed 0 -dep_error 0 -steady 0 -manual 0 -rc 1 lmake src3.hide 13 | 14 | rm src3 15 | ut_launch -dangling 0 -done 1 -failed 0 -steady 2 -manual 0 -rc 0 lmake src3.hide 16 | 17 | ut_launch -dangling 0 -done 1 -failed 0 -steady 0 -manual 0 -rc 0 lmake src3.hide 18 | 19 | echo forget 20 | lshow -dv src3.hide 21 | lforget -d src3.hide 22 | lshow -dv src3.hide 23 | echo forgotten 24 | ut_launch -dangling 0 -done 1 -failed 0 -steady 2 -manual 0 -rc 0 lmake src3.hide 25 | -------------------------------------------------------------------------------- /unit_tests/mk_py3.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | ut_launch -done 2 -may_rerun 0 -new 2 -rerun 0 -steady 0 -was_done 0 lmake src2.import 9 | ut_launch -done 2 -may_rerun 0 -new 1 -rerun 0 -steady 0 -was_done 0 lmake src1.import 10 | 11 | ut_launch -done 1 -steady 0 lmake src1.import 12 | 13 | touch hello.py 14 | ut_launch -done 1 -steady 1 lmake src1.import 15 | 16 | echo >>hello.py 17 | ut_launch -done 1 -changed 1 -rerun 0 -steady 2 lmake src1.import 18 | ut_launch -done 1 -rerun 0 -steady 2 lmake src2.import 19 | -------------------------------------------------------------------------------- /unit_tests/mk_py4.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | ut_launch -done 4 -new 1 lmake src1.cpy2 9 | -------------------------------------------------------------------------------- /unit_tests/mkdir.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step.py' 14 | ) 15 | 16 | from step import step 17 | 18 | class Dut(PyRule): 19 | target = 'dut' 20 | targets = { 'OUT' : r'out/{*:.*}' } 21 | def cmd(): 22 | import os 23 | dir = OUT(f'v{step}') 24 | os.makedirs(dir) 25 | open(f'{dir}/res','w').close() 26 | 27 | else : 28 | 29 | import os 30 | 31 | import ut 32 | 33 | print('step=1',file=open('step.py','w')) 34 | ut.lmake('dut',done=1) 35 | 36 | print('step=2',file=open('step.py','w')) 37 | ut.lmake('dut',done=1) 38 | 39 | assert not os.path.exists('out/v1') 40 | -------------------------------------------------------------------------------- /unit_tests/multi.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'dep1' 14 | , 'dep2' 15 | ) 16 | 17 | class Multi1(Rule) : 18 | target = 'dut' 19 | dep = 'dep1' 20 | cmd = 'cat' 21 | 22 | class Multi2(Rule) : 23 | target = 'dut' 24 | dep = 'dep2' 25 | cmd = 'cat' 26 | 27 | else : 28 | 29 | import ut 30 | 31 | print('dep1',file=open('dep1','w')) 32 | print('dep2',file=open('dep2','w')) 33 | 34 | ut.lmake( 'dut' , multi=1 , rc=1 ) 35 | -------------------------------------------------------------------------------- /unit_tests/name.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step.py' 14 | , 'test.tgz' 15 | ) 16 | 17 | from step import step 18 | 19 | class Expand(Rule) : 20 | name = f'expand{step}' 21 | targets = { 22 | 'TARGET' : r'{File:.*}.tgzdir/{*:.*}' 23 | , 'TRIGGER' : '{File }.tgzdir.trigger' 24 | } 25 | deps = { 'TGZ' : '{File}.tgz' } 26 | cmd = 'tar -xvf {TGZ} -C {File}.tgzdir >{TRIGGER}' 27 | 28 | else : 29 | 30 | import os 31 | 32 | import ut 33 | 34 | os.makedirs('test',exist_ok=True) 35 | open('test/testfile.py','w') 36 | os.system('tar -czf test.tgz test/testfile.py') 37 | 38 | print('step=1',file=open('step.py','w')) 39 | ut.lmake('test.tgzdir.trigger',new=1,done=1) 40 | 41 | print('step=2',file=open('step.py','w')) 42 | ut.lmake('test.tgzdir.trigger') 43 | -------------------------------------------------------------------------------- /unit_tests/numpy.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | 10 | import lmake 11 | 12 | from step import numpy_home 13 | 14 | sys.path.append(numpy_home) # keep local repo as last entry to avoid spurious deps 15 | 16 | import numpy # check we can import numpy 17 | 18 | lmake.manifest = ( 19 | 'Lmakefile.py' 20 | , 'step.py' 21 | ) 22 | 23 | else : 24 | 25 | import os 26 | import os.path as osp 27 | import sys 28 | 29 | import ut 30 | 31 | if 'VIRTUAL_ENV' in os.environ : # manage case where numpy is installed in an activated virtual env 32 | venv = os.environ["VIRTUAL_ENV"] 33 | vi = sys.version_info 34 | sys.path.append(f'{venv}/lib/python{vi.major}.{vi.minor}/site-packages') # mimic open-lmake config 35 | 36 | try : 37 | import numpy 38 | except : 39 | print('numpy not available',file=open('skipped','w')) 40 | exit() 41 | 42 | numpy_home = osp.dirname(osp.dirname(numpy.__file__)) 43 | print(f'numpy_home={numpy_home!r}',file=open('step.py','w')) 44 | 45 | ut.lmake(done=0,new=0) # just check we can load Lmakefile.py 46 | -------------------------------------------------------------------------------- /unit_tests/path.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Base(Rule) : 14 | environ = {'PATH' :'...:foo1'} 15 | environ_resources = {'R' :'foo2' } 16 | paths = {'environ_resources.R':'/' } 17 | 18 | class Test(Base,PyRule) : 19 | target = 'test' 20 | environ = {'PATH':'...:bar1'} 21 | environ_resources = {'R' :'.../bar2'} 22 | def cmd() : 23 | import os 24 | assert os.environ['PATH'].endswith(':foo1:bar1') 25 | assert os.environ['R' ]=='foo2/bar2' 26 | 27 | else : 28 | 29 | import ut 30 | 31 | ut.lmake('test',done=1) 32 | -------------------------------------------------------------------------------- /unit_tests/path_max.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step.py' 14 | ) 15 | 16 | from step import path_max 17 | lmake.config.path_max = path_max 18 | 19 | class Test(Rule) : 20 | target = '12345' 21 | cmd = '' 22 | 23 | else : 24 | 25 | import os 26 | 27 | import ut 28 | 29 | print('path_max=4',file=open('step.py','w')) ; ut.lmake( '12345' , rc=1 ) 30 | print('path_max=5',file=open('step.py','w')) ; ut.lmake( '12345' , done =1 ) 31 | print('path_max=4',file=open('step.py','w')) ; ut.lmake( '12345' , unlink=1 , rc=1 ) 32 | print('path_max=5',file=open('step.py','w')) ; ut.lmake( '12345' , done =1 ) 33 | -------------------------------------------------------------------------------- /unit_tests/pdict.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.utils import pdict 10 | from lmake.rules import PyRule 11 | 12 | lmake.manifest = ('Lmakefile.py',) 13 | 14 | param = pdict(a=1,b=2) 15 | 16 | class Dut(PyRule) : 17 | target = 'dut' 18 | def cmd() : 19 | assert param['a']==1 and param['b']==2 20 | assert param.a ==1 and param.b ==2 21 | 22 | else : 23 | 24 | import ut 25 | 26 | ut.lmake( 'dut' , done=1 ) # check param can be used as a pdict 27 | -------------------------------------------------------------------------------- /unit_tests/phony.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step.py' 14 | ) 15 | 16 | from step import step 17 | 18 | class Test1(PyRule): 19 | targets = { 'GOOD' : ('good','Phony') } 20 | def cmd(): 21 | if step==1 : lmake.depend('bad') 22 | 23 | class Test2(PyRule): 24 | target = 'tgt' 25 | def cmd(): 26 | lmake.depend('good') 27 | if step==1 : lmake.depend('bad') 28 | 29 | else : 30 | 31 | import ut 32 | 33 | print('step=1',file=open('step.py','w')) 34 | ut.lmake('tgt',may_rerun=1,dep_error=1,was_dep_error=1,rc=1) 35 | 36 | print('step=2',file=open('step.py','w')) 37 | ut.lmake('tgt',steady=2) 38 | -------------------------------------------------------------------------------- /unit_tests/ptrace.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Dut(Rule) : 14 | autodep = 'ptrace' 15 | targets = { 16 | 'DUT' : 'dut' 17 | , 'LNK' : 'lnk' 18 | } 19 | cmd = ''' 20 | echo dut >{DUT}.tmp 21 | ln {DUT}.tmp {DUT} # no ln with ptrace in other UT's 22 | rm {DUT}.tmp 23 | ln -s {DUT} {LNK} # no ln -s with ptrace in other UT's 24 | ''' 25 | 26 | class Chk(Rule) : 27 | shell = ('/usr/bin/bash','-e') 28 | target = 'chk' 29 | deps = { 30 | 'DUT' : 'dut' 31 | , 'LNK' : 'lnk' 32 | } 33 | cmd = ''' 34 | [ "$(cat {DUT})" = dut ] 35 | [ "$(cat {LNK})" = dut ] 36 | ''' 37 | 38 | else : 39 | 40 | import ut 41 | 42 | ut.lmake( 'chk' , done=2 ) 43 | -------------------------------------------------------------------------------- /unit_tests/py_rule.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | import time 11 | 12 | from lmake.rules import PyRule 13 | 14 | lmake.manifest = ( 15 | 'Lmakefile.py' 16 | , 'hello.py' 17 | , 'world.py' 18 | ) 19 | 20 | class Import(PyRule) : 21 | target = r'import.{Autodep:\w+}.{Wait:\d+}' 22 | def autodep() : return Autodep 23 | def cmd() : 24 | import hello 25 | time.sleep(int(Wait)) 26 | import world 27 | print(f'{hello.a} {world.b}') 28 | 29 | else : 30 | 31 | import ut 32 | 33 | print('a=1',file=open('hello.py','w')) 34 | print('b=2',file=open('world.py','w')) 35 | 36 | ut.lmake(*(f'import.{ad}.{w}' for ad in lmake.autodeps for w in (1,2,3)),done=3*len(lmake.autodeps),new=2) 37 | -------------------------------------------------------------------------------- /unit_tests/py_venv.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | from lmake.rules import Rule 11 | 12 | lmake.manifest = ('Lmakefile.py',) 13 | 14 | class PyEnv(Rule) : 15 | targets = { 'DST' : r'py_env/{*:.*}' } 16 | side_deps = { 'PY' : ('.','readdir_ok') } # python reads dirs listed in sys.path and this is not handled by PyRule for shell rules 17 | cmd = ''' 18 | ldepend -D py_env 19 | python3 -m venv py_env 20 | . py_env/bin/activate 21 | ''' 22 | 23 | else : 24 | 25 | try : 26 | import venv 27 | venv.main(('py_env_trial',)) 28 | open('py_env_trial/bin/activate') 29 | except : 30 | print('python venv not available',file=open('skipped','w')) 31 | 32 | import ut 33 | 34 | ut.lmake('py_env/pyvenv.cfg',done=1) 35 | -------------------------------------------------------------------------------- /unit_tests/re.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 'Lmakefile.py',) 12 | 13 | class Re(Rule) : 14 | stems = { 15 | 'Dir' : r'(.*/)?' 16 | , 'File' : r'(([^+/])*)' 17 | , 'Opt' : r'([^+]*)' 18 | } 19 | targets = { 20 | 'T1' : '{Dir}{File}+{Opt}.t1' 21 | , 'T2' : ( '{Dir}{File}+{Opt}.t2' , 'incremental' ) 22 | } 23 | cmd = 'echo >{T1} ; echo >>{T2}' 24 | 25 | else : 26 | 27 | import os 28 | 29 | import ut 30 | 31 | ut.lmake( 'd.dir/f+o.t1' , done=1 ) 32 | os.unlink('d.dir/f+o.t1') 33 | ut.lmake( 'd.dir/f+o.t1' , done=1 ) 34 | -------------------------------------------------------------------------------- /unit_tests/readdir.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import gxx 9 | 10 | import lmake 11 | from lmake.rules import Rule,PyRule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'gxx.py' 16 | , 'glob.c' 17 | ) 18 | 19 | class Compile(Rule) : 20 | targets = { 'EXE':'glob' } 21 | deps = { 'SRC':'glob.c' } 22 | cmd = f'{gxx.gxx} -o {{EXE}} -xc {{SRC}}' 23 | 24 | class Dut(Rule) : 25 | target = r'dut.{Dir:a?}.{Ok:ko|ok}' 26 | deps = { 'EXE':'glob' } 27 | cmd = ''' 28 | [ {Ok} = ok ] && ldepend -D {Dir or '.'} 29 | ./{EXE} {Dir} 30 | ''' 31 | 32 | else : 33 | 34 | import os 35 | 36 | import ut 37 | 38 | ut.mk_gxx_module('gxx') 39 | 40 | print(''' 41 | #include 42 | #include 43 | int main( int argc , const char* argv[] ) { 44 | glob_t g ; 45 | if ( argc>=2 && argv[1][0]=='a' ) glob( "a/*" , 0 , NULL , &g ) ; 46 | else glob( "*" , 0 , NULL , &g ) ; 47 | globfree(&g) ; 48 | } 49 | ''',file=open('glob.c','w')) 50 | 51 | os.makedirs('a/b',exist_ok=True) 52 | 53 | ut.lmake( 'dut.a.ko' , 'dut.a.ok' , done=2 , failed=1 , new=1 , rc=1 ) # check readdir-ok is necessary when reading dir a 54 | ut.lmake( 'dut..ko' , 'dut..ok' , done=1 , failed=1 , rc=1 ) # check readdir-ok is necessary when reading top-level 55 | -------------------------------------------------------------------------------- /unit_tests/regexpr.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Dep(Rule) : 14 | target = 'dep_{File:.*}' 15 | cmd = '' 16 | 17 | class DutSh(Rule) : 18 | target = 'dut_sh' 19 | stderr_ok = True 20 | cmd = ''' 21 | cat dep_a1 22 | ldepend --ignore --regexpr 'dep_a.*' 23 | ltarget --ignore --regexpr 'ignore.*' 24 | > ignore_sh 25 | cat dep_a2 || : 26 | cat dep_b1 27 | ''' 28 | 29 | class DutPy(PyRule) : 30 | target = 'dut_py' 31 | stderr_ok = True 32 | def cmd() : 33 | print(open('dep_a3').read()) 34 | lmake.depend(r'dep_a.*' ,ignore=True,regexpr=True) 35 | lmake.target(r'ignore.*',ignore=True,regexpr=True) 36 | try : print(open('dep_a4'),read()) 37 | except : pass 38 | open('ignore_py','w') 39 | print(open('dep_b2').read()) 40 | 41 | else : 42 | 43 | import ut 44 | 45 | ut.lmake( 'dut_sh' , may_rerun=1 , done=3 ) # check dep_a1 & dep_b1 are built, but not dep_a2 46 | ut.lmake( 'dut_py' , may_rerun=2 , done=3 , new=1 ) # check dep_a3 & dep_b2 are built, but not dep_a4 47 | -------------------------------------------------------------------------------- /unit_tests/rename.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | from step import step,python 12 | 13 | lmake.manifest = ('Lmakefile.py','step.py') 14 | 15 | class Cpy( PyRule if python else Rule ) : 16 | targets = { 'DST' : 'test' } 17 | side_targets = { 'SCRATCHPAD' : 'test.sp' } 18 | if python : 19 | def cmd() : 20 | import os 21 | if step!=1 : print('hello',file=open(SCRATCHPAD,'w')) 22 | os.rename(SCRATCHPAD,DST) 23 | else : 24 | if step==1 : cmd = ' mv {SCRATCHPAD} {DST} ' 25 | else : cmd = ' echo hello > {SCRATCHPAD} ; mv {SCRATCHPAD} {DST} ' 26 | else : 27 | 28 | import ut 29 | 30 | for python in (False,True) : 31 | print(f'step=1 ; python={python}',file=open('step.py','w')) 32 | ut.lmake( 'test' , new=python , failed=1 , rc=1 ) # python reads Lmakefile.py to display backtrace 33 | 34 | print(f'step=2 ; python={python}',file=open('step.py','w')) 35 | ut.lmake( 'test' , done=1 , rc=0 ) 36 | -------------------------------------------------------------------------------- /unit_tests/repair.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'hello' 14 | , 'world' 15 | ) 16 | 17 | class Cat(Rule) : 18 | stems = { 19 | 'File1' : r'.*' 20 | , 'File2' : r'.*' 21 | } 22 | deps = { 23 | 'FIRST' : '{File1}' 24 | , 'SECOND' : '{File2}' 25 | } 26 | 27 | class CatSh(Cat) : 28 | target = '{File1}+{File2}_sh' 29 | cmd = 'cat {FIRST} {SECOND}' 30 | 31 | class CatPy(Cat,PyRule) : 32 | target = '{File1}+{File2}_py' 33 | def cmd() : 34 | print(open(FIRST ).read(),end='') 35 | print(open(SECOND).read(),end='') 36 | 37 | else : 38 | 39 | import os 40 | 41 | import ut 42 | 43 | print('hello',file=open('hello','w')) 44 | print('world',file=open('world','w')) 45 | 46 | ut.lmake( 'hello+world_sh' , 'hello+world_py' , done=2 , new=2 ) # check targets are out of date 47 | 48 | assert os.system('lrepair')==0 49 | print('hello2',file=open('hello','w')) 50 | ut.lmake( 'hello+world_sh' , 'hello+world_py' , done=2 , new=2 ) # check targets are remade 51 | 52 | assert os.system('mv LMAKE.bck LMAKE.bck2 ; lrepair')==0 53 | ut.lmake( 'hello+world_sh' , 'hello+world_py' , done=0 , new=2 ) # check targets are up to date 54 | -------------------------------------------------------------------------------- /unit_tests/rerun.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Cat(Rule) : 14 | target = 'dut' 15 | cmd = 'echo dut' 16 | 17 | class Cpy(Rule) : 18 | target = r'{File:.*}.cpy' 19 | max_submits = 1 20 | stderr_ok = True 21 | cmd = ' cat {File} ; : ' 22 | 23 | class Cmp(Rule) : 24 | target = 'dut.ok' 25 | dep = 'dut.cpy' 26 | cmd = '[ $(cat) = dut ]' 27 | 28 | else : 29 | 30 | import ut 31 | 32 | ut.lmake( 'dut.ok' , may_rerun=1 , done=1 , submit_loop=1 , rc=1 ) 33 | -------------------------------------------------------------------------------- /unit_tests/resources.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | n_good = 20 7 | 8 | if __name__!='__main__' : 9 | 10 | import lmake 11 | from lmake.rules import Rule,PyRule 12 | 13 | lmake.manifest = ('Lmakefile.py',) 14 | 15 | lmake.config.backends.local.gnat = 4 16 | 17 | class Test(Rule) : 18 | cmd = 'echo {gnat}' 19 | class Test1(Test) : 20 | target = r'test1.{Tokens:\d}' 21 | resources = { 'gnat': '1' } 22 | 23 | class Test2(Test) : 24 | target = r'test2.{Tokens:\d}' 25 | resources = { 'gnat': '{Tokens}' } 26 | 27 | def compute_gnat() : 28 | return f'{Tokens}' 29 | class Test3(Test) : 30 | target = r'test3.{Tokens:\d}' 31 | resources = { 'gnat': compute_gnat } 32 | 33 | class Test4(PyRule) : 34 | target = r'test4.{Tokens:\d}' 35 | def resources() : return { 'gnat' : f'{Tokens}' } 36 | def cmd () : print(resources['gnat']) 37 | 38 | else : 39 | 40 | import ut 41 | 42 | ut.lmake( 'test1.2' , 'test1.3' , done=2 ) 43 | assert int(open('test1.2').read())==1 44 | assert int(open('test1.3').read())==1 45 | 46 | for x in (2,3,4) : 47 | ut.lmake( f'test{x}.2' , f'test{x}.3' , done=2 ) 48 | assert int(open(f'test{x}.2').read())+int(open(f'test{x}.3').read())==5 49 | -------------------------------------------------------------------------------- /unit_tests/retry.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step.py' 14 | ) 15 | 16 | from step import step 17 | 18 | class Dep1(Rule) : 19 | target = 'dep' 20 | cmd = ': {step}' 21 | 22 | class Bad1(Rule) : 23 | target = 'dut1' 24 | cmd = 'exit 1' 25 | 26 | class Bad2(Rule) : 27 | target = 'dut2' 28 | cmd = 'cat dep ; exit 1' 29 | 30 | class Rerun(Rule) : 31 | target = 'rerun' 32 | cmd = 'cat dep' 33 | 34 | 35 | else : 36 | 37 | import os 38 | 39 | import ut 40 | 41 | print('step=1',file=open('step.py','w')) 42 | 43 | ut.lmake( '-r2' , 'dut1' , retry=2 , failed=3 , rc=1 ) # check retry works as expected 44 | 45 | ut.lmake('dep' , done=1 ) 46 | print('step=2',file=open('step.py','w')) 47 | ut.lmake( '-r2' , 'dut2' , may_rerun=1 , steady=1 , retry=2 , failed=2 , rc=1 ) # check retry also works when dut would be was_failed 48 | 49 | os.unlink('dep') 50 | print('step=3',file=open('step.py','w')) 51 | ut.lmake( '-m1' , 'rerun' , may_rerun=1 , steady=1 , submit_loop=1 , rc=1 ) # observe job in the middle of rerun loop 52 | ut.lmake( 'rerun' , steady=1 ) # observe job at end 53 | -------------------------------------------------------------------------------- /unit_tests/roll_back.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | from step import step 9 | 10 | import lmake 11 | from lmake.rules import Rule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'step.py' 16 | , ('a' if step==1 else 'b') 17 | ) 18 | 19 | class DutCmd(Rule) : 20 | target = 'cmd_{File:.*}' 21 | cmd = 'echo {File} {step}' 22 | 23 | class DutRule(Rule) : 24 | if step==1 : target = 'rule_{File:.*}' 25 | else : target = 'rule_{File:.+}' 26 | cmd = 'echo {File} {step}' 27 | 28 | for d in ('a','b') : 29 | class DutDep(Rule) : 30 | name = f'DutDep {d}' 31 | target = 'dep_{File:.*}' 32 | deps = {'DEP':d} 33 | cmd = 'echo {File} {DEP}' 34 | 35 | else : 36 | 37 | import ut 38 | 39 | print( file=open('a' ,'w')) 40 | print( file=open('b' ,'w')) 41 | print('step=1',file=open('step.py','w')) 42 | ut.lmake( 'cmd_light' , 'cmd_heavy' , 'rule_light' , 'rule_heavy' , 'dep_light' , 'dep_heavy' , done=6 , new=1 ) 43 | 44 | print('step=2',file=open('step.py','w')) 45 | ut.lmake( 'cmd_light' , 'rule_light' , 'dep_light' , done=3 , new=1 ) 46 | 47 | print('step=1',file=open('step.py','w')) 48 | ut.lmake( 'cmd_light' , 'cmd_heavy' , 'rule_light' , 'rule_heavy' , 'dep_light' , 'dep_heavy' , done=3 , new=1 ) # check light are rebuilt and heavy are not 49 | -------------------------------------------------------------------------------- /unit_tests/rules.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | n = 500 7 | 8 | if __name__!='__main__' : 9 | 10 | import lmake 11 | from lmake.rules import Rule,PyRule 12 | 13 | lmake.manifest = ('Lmakefile.py',) 14 | 15 | for r in range(n) : # ensure rules recording can handle a lot of rules 16 | class Sfx(Rule) : 17 | name = f'Sfx{r}' 18 | target = fr'{{File:.*}}.{r}' 19 | cmd = '' 20 | class Pfx(Rule) : 21 | name = f'Pfx{r}' 22 | target = fr'{r}.{{File:.*}}' 23 | cmd = '' 24 | 25 | else : 26 | 27 | import ut 28 | 29 | ut.lmake() # ensure no crash and reasonable time 30 | -------------------------------------------------------------------------------- /unit_tests/scratchpad.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | from lmake import multi_strip 11 | from lmake.rules import Rule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'hello' 16 | ) 17 | 18 | for ad in lmake.autodeps : 19 | class Cat(Rule) : 20 | name = f'cat {ad}' 21 | target = fr'{{File:.*}}.{ad}' 22 | dep = '{ File }' 23 | autodep = ad 24 | cmd = multi_strip(''' 25 | echo tmp > scratch.$$ 26 | cat 27 | rm scratch.$$ 28 | ''') 29 | 30 | else : 31 | 32 | import ut 33 | 34 | print('hello',file=open('hello','w')) 35 | 36 | ut.lmake( *(f'hello.{ad}' for ad in lmake.autodeps) , done=len(lmake.autodeps) , new=1 ) # check scratchpad does not prevent job from running normally 37 | -------------------------------------------------------------------------------- /unit_tests/speculate.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'src' 14 | ) 15 | 16 | class Bad(Rule) : 17 | target = 'bad' 18 | dep = 'src' 19 | cmd = 'exit 1' 20 | 21 | class Dut(Rule) : 22 | target = 'dut' 23 | cmd = 'grep read_bad src && cat bad ; exit 0' 24 | 25 | else : 26 | 27 | import ut 28 | 29 | print('read_bad1',file=open('src','w')) 30 | ut.lmake( 'dut' , new=1 , may_rerun=1 , failed=1 , was_dep_error=1 , rc=1 ) # check targets are out of date 31 | 32 | print('read_bad2',file=open('src','w')) 33 | ut.lmake( 'dut' , changed=1 , failed=1 , was_failed=1 , dep_error=1 , rc=1 ) # failed : when bad is computed speculatively, was_failed : when bad is finally known to be actually needed 34 | 35 | print('3',file=open('src','w')) 36 | ut.lmake( 'dut' , changed=1 , failed=1 , done=1 ) 37 | -------------------------------------------------------------------------------- /unit_tests/src.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'world' 14 | , 'world.ref' 15 | ) 16 | 17 | class HelloWorld(Rule) : 18 | targets = { 19 | 'HELLO' : 'hello' 20 | , 'WORLD' : ( 'world' , 'SourceOk' ) 21 | } 22 | cmd = 'echo hello >{HELLO} ; echo world > {WORLD}' 23 | 24 | class Cmp(Rule) : 25 | target = r'{File:.*}.ok' 26 | deps = { 27 | 'ACTUAL' : '{File}' 28 | , 'REF' : '{File}.ref' 29 | } 30 | cmd = 'diff {REF} {ACTUAL} >&2' 31 | 32 | 33 | else : 34 | 35 | import ut 36 | 37 | print('moon' ,file=open('world' ,'w')) 38 | print('world',file=open('world.ref','w')) 39 | 40 | ut.lmake( 'hello' , done=1 , new=0 ) # check rule is ok despite writing to source world 41 | ut.lmake( 'world.ok' , done=1 , new=1 ) # check world has been updated 42 | -------------------------------------------------------------------------------- /unit_tests/static_deps.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | import sys 11 | 12 | from lmake.rules import Rule 13 | 14 | lmake.manifest = ( 15 | 'Lmakefile.py' 16 | , 'dummy' 17 | ) 18 | 19 | class Dut(Rule) : 20 | target = r'dut.{Autodep:\w+}' 21 | deps = { 'Dummy' : 'dummy' } 22 | autodep = '{Autodep}' 23 | cmd = '' 24 | 25 | else : 26 | 27 | autodeps = ('none',*lmake.autodeps) 28 | 29 | import ut 30 | 31 | print(f'1',file=open('dummy','w')) 32 | ut.lmake( *(f'dut.{ad}' for ad in autodeps) , new=1 , done=len(autodeps) ) 33 | 34 | print(f'2',file=open('dummy','w')) 35 | ut.lmake( *(f'dut.{ad}' for ad in autodeps) , changed=1 , steady=1 ) # only case autodep=none believes that dut needs dummy 36 | -------------------------------------------------------------------------------- /unit_tests/sub_file.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'dep' 14 | , 'ref' 15 | ) 16 | 17 | class Dut(Rule) : 18 | target = 'dut' 19 | stderr_ok = True 20 | cmd = 'cat dep/sub ; cat dep' 21 | 22 | class Test(Rule) : 23 | target = 'test' 24 | deps = { 25 | 'DUT' : 'dut' 26 | , 'REF' : 'ref' 27 | } 28 | cmd = 'diff {REF} {DUT}' 29 | 30 | else : 31 | 32 | import ut 33 | 34 | print('good',file=open('ref','w')) 35 | 36 | print('bad',file=open('dep','w')) 37 | ut.lmake( 'dut' , done=1 , new=1 ) 38 | 39 | print('good',file=open('dep','w')) 40 | ut.lmake( 'test' , done=2 , changed=1 , new=1 ) # check dut is rerun, i.e. that depending on dep/sub does not suppress dep as a dependency 41 | -------------------------------------------------------------------------------- /unit_tests/sub_repos.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | try : depth 9 | except : depth = 0 # define depth for top level 10 | 11 | import lmake 12 | from lmake.rules import Rule 13 | 14 | lmake.config.sub_repos = ('a','b') # for top level only, overwritten in sub-repos 15 | 16 | lmake.manifest = ('Lmakefile.py',) 17 | 18 | class Dut(Rule) : 19 | target = 'dut' 20 | cmd = f'echo {depth}' 21 | 22 | class Test(Rule) : 23 | target = r'{File:.*}.ok' 24 | dep = '{File }' 25 | cmd = f'[ $(cat) = {depth} ]' 26 | 27 | else : 28 | 29 | import os 30 | 31 | import ut 32 | 33 | makefile = open('Lmakefile.py').read() 34 | os.makedirs('a' ,exist_ok=True) ; print(f"depth=1\n{makefile}lmake.config.sub_repos = ('b',)",file=open('a/Lmakefile.py' ,'w')) 35 | os.makedirs('b' ,exist_ok=True) ; print(f"depth=1\n{makefile}lmake.config.sub_repos = ()" ,file=open('b/Lmakefile.py' ,'w')) 36 | os.makedirs('a/b',exist_ok=True) ; print(f"depth=2\n{makefile}lmake.config.sub_repos = ()" ,file=open('a/b/Lmakefile.py','w')) 37 | 38 | ut.lmake('dut.ok','a/dut.ok','b/dut.ok','a/b/dut.ok',done=8) 39 | -------------------------------------------------------------------------------- /unit_tests/submit_loop.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step.py' 14 | ) 15 | 16 | from step import step 17 | 18 | class Dep(Rule) : 19 | target = r'dep.{N:\d+}' 20 | cmd = '' 21 | 22 | class SubmitLoop(PyRule) : 23 | if step==2 : max_submits = 2 24 | target = 'submit_loop' 25 | def cmd() : 26 | for i in range(9) : 27 | open(f'dep.{i}') # discover deps one by one 28 | 29 | class Dut(Rule) : 30 | target = 'dut' 31 | cmd = ''' 32 | cat submit_loop 33 | ''' 34 | 35 | else : 36 | 37 | import os 38 | import subprocess as sp 39 | 40 | import ut 41 | 42 | print('step=1',file=open('step.py','w')) 43 | ut.lmake( 'submit_loop' , new=1 , may_rerun=9 , done=10 ) 44 | 45 | for i in range(9) : os.unlink(f'dep.{i}') 46 | sp.run(('lforget','-d','submit_loop')) 47 | print('step=2',file=open('step.py','w')) 48 | ut.lmake( 'dut' , may_rerun=3 , steady=2 , submit_loop=1 , was_dep_error=1 , rc=1 ) # check no crash 49 | -------------------------------------------------------------------------------- /unit_tests/subprocess.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ( 'Lmakefile.py',) 12 | 13 | class Dut(Rule) : 14 | target = 'dut' 15 | cmd = ''' 16 | setsid sleep 5 & 17 | wait $! 18 | ''' 19 | 20 | else : 21 | 22 | import ut 23 | 24 | ut.lmake( 'dut' , done=1 ) # goal is to interrupt this job 25 | -------------------------------------------------------------------------------- /unit_tests/suspend.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | from lmake.rules import Rule,PyRule 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'step.py' 15 | ) 16 | 17 | from step import step 18 | 19 | class Bad(Rule) : 20 | target = 'bad' 21 | cmd = 'exit 1' 22 | 23 | class Dut(PyRule) : 24 | target = 'dut' 25 | def cmd() : 26 | if step==1 : 27 | open('bad') 28 | else : 29 | with lmake.Autodep(False) : open('bad') 30 | 31 | else : 32 | 33 | if lmake.Autodep.IsFake : 34 | print('clmake not available',file=open('skipped','w')) 35 | exit() 36 | 37 | import ut 38 | 39 | print('step=1',file=open('step.py','w')) ; ut.lmake( 'dut' , new=1 , may_rerun=1 , failed=1 , was_dep_error=1 , rc=1 ) 40 | print('step=2',file=open('step.py','w')) ; ut.lmake( 'dut' , steady=1 , rc=0 ) 41 | -------------------------------------------------------------------------------- /unit_tests/sym_lnk.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'hello' 14 | , 'world' 15 | ) 16 | 17 | class Lnk(Rule) : 18 | targets = { 'LNK' : r'{File:.*}.lnk' } 19 | deps = { 'SRC' : '{File }' } 20 | cmd = 'ln -s {SRC} {LNK}' 21 | 22 | class Cat(Rule) : 23 | target = r'hello+{File:.*}' 24 | cmd = 'cat hello.lnk {File}' 25 | 26 | class Abc(Rule) : 27 | targets = { 'ABC' : 'a/b/c' } 28 | cmd = 'ln -s ../../d/e {ABC}' 29 | 30 | class De(Rule) : 31 | targets = { 'DE' : 'd/e' } 32 | cmd = 'echo abc > a/b/c' 33 | 34 | class A(Rule) : 35 | targets = { 'A' : 'a2' } 36 | cmd = 'ln -s b2 {A}' 37 | 38 | class B(Rule) : 39 | targets = { 'B' : 'b2' } 40 | cmd = 'echo a2 > a2' 41 | 42 | else : 43 | 44 | import ut 45 | 46 | print('hello',file=open('hello','w')) 47 | print('world',file=open('world','w')) 48 | 49 | ut.lmake( 'hello+world' , may_rerun=1 , done=2 , new=2 ) # check link is distinguished from non-existent 50 | 51 | ut.lmake( 'd/e' , may_rerun=1 , done=2 ) # check we acquire dep to a/b/c although we write to it (a/b/c cant be written up front) 52 | ut.lmake( 'b2' , may_rerun=1 , done=2 ) # check we acquire dep to a although we write to it (a2 can be written up front) 53 | -------------------------------------------------------------------------------- /unit_tests/tar.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | import os 10 | 11 | import lmake 12 | from lmake import multi_strip 13 | from lmake.rules import Rule,PyRule 14 | 15 | lmake.manifest = ('Lmakefile.py',) 16 | 17 | file = 'a/b/c' 18 | class Tar(Rule) : 19 | targets = { 'TAR' : r'hello.tar{*:.*}' } 20 | environ = { 'REPO_ROOT' : '$REPO_ROOT' } 21 | cmd = multi_strip(''' 22 | cd $TMPDIR 23 | mkdir -p $(dirname {file}) 24 | echo yes >{file} 25 | tar cf $REPO_ROOT/hello.tar {file} 26 | ''') 27 | class Untar(PyRule) : 28 | targets = { 29 | 'TARGET' : r'{File:.*}.tardir/{*:.*}' 30 | , 'PROTO' : r'{File:.*}.proto' 31 | } 32 | deps = { 'TAR' : '{File}.tar' } 33 | def cmd() : 34 | print(TARGET(''),file=open(PROTO,'w')) 35 | os.system(f'tar mxaf {TAR} -C {File}.tardir') 36 | 37 | class Cpy(PyRule) : 38 | target = 'cpy' 39 | dep = f'hello.tardir/{file}' 40 | def cmd() : 41 | assert open('hello.proto').read()=='hello.tardir/\n' 42 | print(sys.stdin.read()) 43 | 44 | else : 45 | 46 | import ut 47 | 48 | ut.lmake( 'cpy' , done=3 ) 49 | -------------------------------------------------------------------------------- /unit_tests/target.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import sys 9 | 10 | import lmake 11 | from lmake.rules import Rule,PyRule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'hello' 16 | , 'side.py' 17 | , 'side.sh' 18 | ) 19 | 20 | class Base(Rule) : 21 | stems = { 22 | 'File' : r'.*' 23 | , 'YesNo' : r'[yn]' 24 | } 25 | dep = '{File}' 26 | autodep = 'none' 27 | 28 | class ShCpy(Base) : 29 | targets = { 'DST' : '{File}.sh.{YesNo}.cpy' } 30 | cmd = ''' 31 | [ {YesNo} = y ] && ( ltarget {DST} ; cat>{DST} ) 32 | ltarget -s side.sh ; echo side > side.sh 33 | ''' 34 | 35 | class PyCpy(Base,PyRule) : 36 | targets = { 'DST' : '{File}.py.{YesNo}.cpy' } 37 | def cmd() : 38 | if YesNo=='y' : 39 | lmake.target(DST) 40 | open(DST,'w').write(sys.stdin.read()) 41 | lmake.target('side.py',source_ok=True) 42 | print('side',file=open('side.py','w')) 43 | 44 | else : 45 | 46 | import ut 47 | 48 | print(f'hello' ,file=open('hello' ,'w')) 49 | print(f'not side',file=open('side.py','w')) 50 | print(f'not side',file=open('side.sh','w')) 51 | 52 | ut.lmake( 'hello.sh.y.cpy' , 'hello.py.y.cpy' , done =2 , new=1 ) 53 | ut.lmake( 'hello.sh.n.cpy' , 'hello.py.n.cpy' , failed=2 , new=0 , rc=1 ) 54 | 55 | assert open('side.py').read()=='side\n' 56 | assert open('side.sh').read()=='side\n' 57 | -------------------------------------------------------------------------------- /unit_tests/target2.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step.py' 14 | , 'a_source' 15 | ) 16 | 17 | from step import step 18 | 19 | class A(PyRule): 20 | target = 'src_name' 21 | if step==1 : 22 | def cmd() : 23 | print('a_source_with_typo') 24 | else : 25 | def cmd() : 26 | print('a_source') 27 | 28 | class Dut(PyRule): 29 | target = 'dut' 30 | deps = { 'SRC_NAME':'src_name' } 31 | def cmd() : 32 | src = open(SRC_NAME).read().strip() 33 | lmake.target( src , source_ok=True ) 34 | open('a_source','w').write('something') 35 | 36 | else : 37 | 38 | import ut 39 | 40 | print('hello',file=open('a_source','w')) 41 | 42 | print('step=1',file=open('step.py','w')) 43 | ut.lmake( 'dut' , done=1 , failed=1 , rc=1 ) 44 | 45 | print('step=2',file=open('step.py','w')) 46 | ut.lmake( 'dut' , done=1 , steady=1 ) 47 | 48 | -------------------------------------------------------------------------------- /unit_tests/test1.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | ut_launch -done 3 -new 1 lmake src1.2.wait.cpy & 9 | sleep 1 10 | x=$(lshow -r src1.2.wait.cpy) 11 | case $x in 12 | *src1.2.wait*) ;; 13 | * ) echo missing src1.2.wait in lshow -r output >&2 ; exit 1 ;; 14 | esac 15 | ut_launch -done 2 lmake src1.3.wait 16 | wait %1 17 | -------------------------------------------------------------------------------- /unit_tests/test2.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | TARGET=src1.w6.wait 9 | date +%T.%N >&2 10 | lmake $TARGET & 11 | date +%T.%N >&2 12 | sleep 3 13 | date +%T.%N >&2 14 | kill -2 %1 15 | date +%T.%N >&2 16 | echo 'Wait.stems["Wait"] = "("+Wait.stems["Wait"]+")"' >> Lmakefile.py 17 | wait %1 || : 18 | date +%T.%N >&2 19 | ut_launch -done 1 -steady 2 -manual 0 lmake $TARGET 20 | date +%T.%N >&2 21 | -------------------------------------------------------------------------------- /unit_tests/test3.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | ut_launch -done 1 -new 1 -changed 1 -dep_error 1 -rc 1 lmake src1.6r.wait & 9 | sleep 4 10 | echo new1 > src1 ; date 11 | wait %1 12 | 13 | ut_launch -done 1 -steady 0 -dep_error 0 -rc 0 lmake src1.6r.wait 14 | -------------------------------------------------------------------------------- /unit_tests/test4.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | ut_launch -done 2 -new 1 lmake src1.6.wait & 9 | sleep 2 10 | lmake src1.4.wait & 11 | sleep 2 12 | kill -2 %2 ; date 13 | wait %2 || : 14 | ut_launch -done 2 lmake src1.4.wait 15 | wait %1 16 | -------------------------------------------------------------------------------- /unit_tests/test5.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | ut_launch -killed 2 -done 1 -new 1 -rc -2 lmake src1.8.wait src1.7.wait & 9 | sleep 1 10 | ut_launch -done 2 lmake -o src1.8.wait & 11 | sleep 4 12 | kill -2 %1 13 | wait %1 14 | wait %2 15 | -------------------------------------------------------------------------------- /unit_tests/timeout.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Timeout(Rule) : 14 | target = r'dut.{Sleep:\d+}' 15 | timeout = 3 16 | cmd = 'sleep {Sleep}' 17 | 18 | else : 19 | 20 | import ut 21 | 22 | ut.lmake( 'dut.1' , 'dut.5' , done=1 , failed=1 , rc=1 ) # dut.7 times out 23 | -------------------------------------------------------------------------------- /unit_tests/trace_exec.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | import lmake 7 | 8 | if __name__!='__main__' : 9 | 10 | from lmake.rules import Rule,TraceRule 11 | 12 | lmake.manifest = ( 13 | 'Lmakefile.py' 14 | , 'sub_script' 15 | ) 16 | 17 | class Dut(TraceRule) : 18 | targets = { 'DUT' : 'dut' } 19 | deps = { 'EXE' : 'sub_script' } 20 | tmp_view = '/tmp' 21 | cmd = ''' 22 | ./{EXE} > $TMPDIR/tmp 23 | cat $TMPDIR/tmp >{DUT} # check tracing does not prevent redirection of stdout 24 | ''' 25 | 26 | class Test(Rule) : 27 | target = 'test' 28 | dep = 'dut' 29 | cmd = ''' 30 | ./sub_script >$TMPDIR/1 31 | cat >$TMPDIR/2 32 | diff $TMPDIR/1 $TMPDIR/2 >&2 33 | ''' 34 | 35 | else : 36 | 37 | import os 38 | import subprocess as sp 39 | 40 | from lmake.utils import multi_strip , indent 41 | 42 | import ut 43 | 44 | print('echo hello',file=open('sub_script','w')) 45 | os.chmod('sub_script',0o755) 46 | 47 | ut.lmake( 'test' , done=2 , new=1 ) 48 | 49 | log = sp.check_output(('lshow','-o','dut'),universal_newlines=True) 50 | ref = indent(multi_strip(''' 51 | + ./sub_script 52 | + cat /tmp/tmp 53 | ''')) 54 | assert log==ref,f'log={log!r} ref={ref!r}' 55 | -------------------------------------------------------------------------------- /unit_tests/transient.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class Rc(Rule) : 14 | target = r'{File:[^/]*}.{Rc:\d+}' 15 | cmd = 'exit {Rc}' 16 | 17 | class Ko(PyRule) : 18 | target = r'{File:[^/]*}.ko' 19 | def cmd() : 20 | try : print(open(f'{File}.0').read()) 21 | except : print(open(f'{File}.1').read()) 22 | finally : print(open(f'{File}.2').read()) 23 | 24 | else : 25 | 26 | import ut 27 | 28 | ut.lmake( 'test.ko' , new=... , may_rerun=1 , done=1 , failed=2 , was_failed=1 , dep_error=1 , rc=1 ) 29 | -------------------------------------------------------------------------------- /unit_tests/unstable.script: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 4 | # Copyright (c) 2023-2025 Doliam 5 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 6 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | 8 | echo 1 > src1 9 | 10 | ut_launch -done 2 -rerun 0 -new 1 -steady 0 -rc 0 lmake src1.6r.wait & 11 | sleep 3 12 | touch src1 13 | wait %1 14 | ut_launch -done 1 -changed 0 -rc 0 lmake src1.6r.wait 15 | 16 | echo 2 > src1 17 | 18 | ut_launch -done 1 -rerun 0 -changed 1 -new 0 -dep_error 1 -rc 1 lmake src1.6r.wait & 19 | sleep 3 20 | echo >> src1 21 | wait %1 22 | ut_launch -done 1 -changed 0 -rc 0 lmake src1.6r.wait 23 | 24 | echo 3 > src1 25 | 26 | ut_launch -done 1 -rerun 0 -changed 2 -new 0 -dep_error 1 -rc 1 lmake src1.r6.wait & 27 | sleep 3 28 | echo >> src1 29 | wait %1 30 | ut_launch -done 2 -changed 0 -rc 0 lmake src1.r6.wait 31 | -------------------------------------------------------------------------------- /unit_tests/uphill.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | class A(Rule) : 14 | targets = { 'A' : r'a/{*:.*}' } 15 | cmd = 'exit 1' 16 | 17 | class E(Rule) : 18 | targets = { 'E' : ('e','phony') } 19 | cmd = '' 20 | 21 | class Ef(Rule) : 22 | target = 'e/f' 23 | cmd = '' 24 | 25 | class D(Rule): 26 | target = 'res' 27 | cmd = 'cat a/b/c/d e/f' 28 | 29 | else : 30 | 31 | import ut 32 | 33 | ut.lmake( 'res' , may_rerun=1 , failed=1 , steady=1 , done=1 , was_dep_error=1 , rc=1 ) 34 | -------------------------------------------------------------------------------- /unit_tests/use_script.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule,PyRule 10 | 11 | lmake.manifest = ( 12 | 'Lmakefile.py' 13 | , 'step-0' 14 | ) 15 | 16 | class Step(Rule) : 17 | use_script = True 18 | target = r'step-{Step:\d+}' 19 | dep = 'step-{int(Step)-1}' 20 | cmd = 'echo {Step}' 21 | 22 | class Test(Rule) : 23 | target = r'test-{Step:\d+}' 24 | dep = 'step-{Step}' 25 | cmd = '[ $(cat) = {Step} ]' 26 | 27 | else : 28 | 29 | import ut 30 | 31 | print('0',file=open('step-0','w')) 32 | 33 | n = 10 34 | 35 | ut.lmake( f'step-{n}' , done=n , new=1 ) 36 | ut.lmake( *(f'test-{i+1}' for i in range(n) ) , done=n ) 37 | -------------------------------------------------------------------------------- /unit_tests/user_env.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | from lmake.rules import Rule 10 | 11 | lmake.manifest = ('Lmakefile.py',) 12 | 13 | var = lmake.user_environ.VAR 14 | 15 | class Dut(Rule) : 16 | target = 'dut' 17 | cmd = 'echo {var}' 18 | 19 | class Test(Rule) : 20 | target = r'test.{Ref:.*}' 21 | dep = 'dut' 22 | cmd = '[ $(cat) = {Ref} ]' 23 | 24 | else : 25 | 26 | import os 27 | 28 | import ut 29 | 30 | os.environ['VAR'] = 'val1' ; ut.lmake( 'test.val1' , done=2 ) 31 | os.environ['VAR'] = 'val2' ; ut.lmake( 'test.val2' , done=2 ) # check config is re-read 32 | -------------------------------------------------------------------------------- /unit_tests/valgrind.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | valgrind = '/usr/bin/valgrind' 7 | 8 | import lmake 9 | 10 | if __name__!='__main__' : 11 | 12 | from lmake.rules import Rule,PyRule 13 | 14 | lmake.manifest = ('Lmakefile.py',) 15 | 16 | class Dut1(Rule) : 17 | autodep = 'ptrace' # XXX! : generates a free(): invalid pointer with ld_audit 18 | targets = {'LOG':'log'} 19 | cmd = '{valgrind} --log-file={LOG} --tool=memcheck hostname' 20 | 21 | class Dut2(Rule) : 22 | max_submits = 2 23 | autodep = 'ptrace' # valgrind does not go through libc to write its output 24 | targets = { 'LOGS' : r'logdir/{*:.*}' } 25 | cmd = ''' 26 | {valgrind} --log-file={LOGS('log')} --tool=memcheck hostname 27 | ''' 28 | 29 | else : 30 | 31 | import os 32 | import os.path as osp 33 | 34 | if 'ptrace' not in lmake.autodeps : 35 | print('ptrace not available',file=open('skipped','w')) 36 | exit() 37 | 38 | if not osp.exists(valgrind) : 39 | print('valgrind not available',file=open('skipped','w')) 40 | exit() 41 | 42 | import ut 43 | 44 | ut.lmake( 'log' , done=1 ) 45 | ut.lmake( 'logdir/log' , done=1 ) 46 | -------------------------------------------------------------------------------- /unit_tests/vfork.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import subprocess as sp 9 | 10 | import lmake 11 | from lmake.rules import Rule,PyRule 12 | 13 | lmake.manifest = ( 14 | 'Lmakefile.py' 15 | , 'dep' 16 | , 'sub/mrkr' 17 | ) 18 | 19 | class Bad(Rule) : 20 | target = 'sub/dep' 21 | cmd = 'exit 1' 22 | 23 | class Dut(PyRule) : 24 | target = 'dut' 25 | def cmd() : 26 | sp.run(('hostname',),cwd='sub') 27 | open('dep') # create a dep to ensure it is not seen in the sub directory after vfork with chdir 28 | 29 | else : 30 | 31 | import os 32 | import os.path as osp 33 | 34 | import ut 35 | 36 | os.makedirs('sub',exist_ok=True) 37 | open('sub/mrkr','w') 38 | open('dep' ,'w') 39 | 40 | ut.lmake( 'dut' , done=1 , new=1 ) # check target is written at the top level despites being after a vfork that does a cd in the child 41 | -------------------------------------------------------------------------------- /unit_tests/wide.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import os.path as osp 9 | import socket 10 | 11 | import lmake 12 | from lmake.rules import PyRule 13 | 14 | if 'slurm' in lmake.backends and osp.exists('/etc/slurm/slurm.conf') : 15 | lmake.config.backends.slurm = { 16 | 'interface' : lmake.user_environ.get('LMAKE_INTERFACE',socket.gethostname()) 17 | , 'use_nice' : True 18 | , 'n_max_queued_jobs' : 10 19 | } 20 | backend = 'slurm' 21 | else : 22 | backend = 'local' 23 | 24 | 25 | lmake.manifest = ('Lmakefile.py',) 26 | 27 | lmake.config.backends.local.cpu = 10 28 | lmake.config.trace.n_jobs = 10000 29 | 30 | class GenFile(PyRule) : 31 | target = r'file_{:\d+}' 32 | backend = backend 33 | resources = { 'mem' : '1M' } 34 | def cmd() : 35 | for x in range(1000) : print(x) 36 | 37 | class Trig(PyRule) : 38 | target = r'out_{P:\d+}_{N:\d+}' 39 | def cmd() : 40 | p = int(P) 41 | if p : lmake.depend(*(f'out_{p-1}_{x}' for x in range(int(N)+1))) 42 | else : lmake.depend(*(f'file_{x}' for x in range(int(N)+1))) 43 | 44 | else : 45 | 46 | import ut 47 | 48 | n = 20 49 | p = 40 50 | d = ut.lmake( f'out_{p}_{n}' , may_rerun=... , rerun=... , was_done=... , done=... , steady=... ) 51 | expected_done = n*p+p+n+2 52 | assert d['was_done']+d['done']+d['steady']==n*p+p+n+2,f'bad counts : {d} expected done : {expected_done}' 53 | -------------------------------------------------------------------------------- /unit_tests/wrong_target.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__!='__main__' : 7 | 8 | import lmake 9 | 10 | lmake.manifest = ('Lmakefile.py',) 11 | 12 | else : 13 | 14 | import ut 15 | 16 | ut.lmake( '../dut' , rc=1 ) # file is outside repo 17 | ut.lmake( '/dut' , rc=1 ) # . 18 | -------------------------------------------------------------------------------- /unit_tests/xxh.py: -------------------------------------------------------------------------------- 1 | # This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) 2 | # Copyright (c) 2023-2025 Doliam 3 | # This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). 4 | # This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 5 | 6 | if __name__=='__main__' : 7 | 8 | import os 9 | import subprocess as sp 10 | 11 | import lmake 12 | 13 | crc_py = lmake.xxhsum_file('Lmakefile.py') 14 | crc_sh = sp.check_output(('xxhsum','Lmakefile.py'),universal_newlines=True).strip() 15 | 16 | assert crc_py==crc_sh 17 | --------------------------------------------------------------------------------