Execution
Handling an access
At first glance, recognizing a target from a dep when a job runs seems pretty easy when the accesses to the disk can be traced : reading a file is a dep, writing to it is a target. And this is what is done informally, but there are a lot of corner cases.
The specification devised hereinafter has been carefully thought to allow open-lmake to run adequate jobs to reach a stable state from any starting point. More specifically, think of the following sequence:
git clean -ffdx
lmake foo
git pull
lmake foo
The second lmake foo command is supposed to do the minimum work to reach the same content of foo as would be obtained with the sequence:
git pull
git clean -ffdx
lmake foo
This what stable state means : the content of foo is independent of the history and only depends on the rules and the content of sources, both being managed through git in this example.
In this specification, dirs are ignored (i.e. the presence or content of a dir has no impact) and symbolic links are similar to regular files whose content is the link itself.
Reading and writing files
The first point is to precisely know what reading and writing mean.
Writing to file foo means:
- A system call that writes or initiate writing to
foo, e.g.open("foo",O_WRONLY|O_TRUNC)orsymlink(...,"foo"), assuming theautodeprule attribute is not set to'none'. - Unlinking
foo, e.g.unlink("foo"), is also deemed to be writing to it. - A call to
lmake.target('foo',write=True). - The execution of
ltarget -W foo. - Under the condition that these actions are not preceded by a call to
lmake.target('foo',ignore=True)or the execution ofltarget -I foo. - Also under the condition that
foodoes not match atargetsorside_targetsentry with theIgnoreflag set. - Also under the condition that
foolies in the repo (i.e. under the dir containingLmakefile.pybut not in itsLMAKE/lmakesub-dir).
Reading file foo means :
- A system call that reads or initiate reading
foo, e.g.open("foo",O_RDONLY),readlink("foo",...)orstat("foo",...), assuming theautodeprule attribute is not set to'none'. - Unless the
config.link_supportattribute is set to'none', any access (reading or writing) tofoowhich follows symlinks is an implicitreadlink. - Unless the
config.link_supportattribute is set to'file'or'none', any access (reading or writing) tofoo, whether it follows symlinks or not, is an implicitreadlinkof all dirs leading to it. - Note that some system calls can be both a read and a write, e.g.
open("foo", O_RDWR)but alsorename("foo",...). In that case, the read occurs before the write. - A call to
lmake.depend('foo',read=True). - The execution of
ldepend -R foo. - Under the condition that these actions are not preceded by a call to
lmake.depend('foo',ignore=True)or the execution ofldepend -I foo. - Also under the condition that
foois not listed indepsor matches aside_depsentry, with theIgnoreflag set. - Also under the condition that
foolies in the repo (i.e. under the dir containingLmakefile.pybut not in itsLMAKE/lmakesub-dir) or in a source dir.
Reading a dir
A dir foo is read when files it contains are listed, which occur when:
- A system call that reads dir
foo, e.g. getdents(2). - A libc call that reads dir
foo, e.g. readdir(3) or glob(3) (in which its pattern argument requires readingfoo). - Under the condition that these actions are not preceded by a call to
lmake.target('foo',ignore=True)or the execution ofltarget -I foo. - Also under the condition that neither
lmake.target('foo',incremental=True)was called norltarget -i fooexecuted. - Also under the condition that
foodoes not match atargetsorside_targetsentry with theignoreorincrementalflags set. - Also under the condition that
foolies in the repo (i.e. under the dir containingLmakefile.pybut not in itsLMAKE/lmakesub-dir) or is the repo root dir.
Although dirs do not exist for open-lmake, reading dir foo is an error unless:
- Set the
readdir_okflag as a rule attribute. - Passing the
readdir_okflag in thetargets,side_targetsorside_depsentry. - Calling
lmake.depend('foo',readdir_ok=True)or executingldepend -D foo. - Calling
lmake.target('foo',readdir_ok=True)or executingltarget -D foo.
Note that the lmake.PyRule base class sets the the readdir_ok flag on dirs mentioned in sys.path when executing import in python3.
This is because python3 optimizes imports by pre-reading these dirs.
Such restrictions ensure the reliability of job execution as the content of a dir is mostly unpredictable as it depends on the past history: files may or may not have been already built, or previously built files that are now non-buildable may still exist.
Ideally, listing a dir would lead to all buildable files (or sub-dirs), but this is not doable in the generic case as such list may be infinite. So open-lmake reverts to letting the user deal with this question, using an opt-in approach so the user cannot miss it.
Note that if such a dir is marked as incremental, the user already has the responsibility of handling its past history and there is no need for an additional flag.
Being a target
A file may be a target from the begining of the job execution, or it may become a target during job execution. In the latter case, it is not a target until the point where it becomes one. A file cannot stop being a target: once it has become a target, this is until the end of the job execution.
A file is a target from the begining of the job execution if it matches a targets or side_targets entry.
A file becomes a target when :
- It is written to (with the meaning mentioned above)..
lmake.targetorltargetis called unless the flagsource_okis finally set (i.e. including if set independently).
Being a dep
A file may be a dep from the begining of the job execution, or it may become a dep during job execution.
A file cannot stop being a dep : once it has become a dep, this is until the end of the job execution.
A file is a dep from the begining of the job execution if it listed as a deps in the rule.
A file becomes a dep when it is read (with the meaning mentioned above) while not a target at that time.
Errors
Some cases lead to errors, independently of the user script.
The first case is when there is clash between static declarations.
targets, side_targets, side_deps entries may or may not contain star stems.
In the latter case, and including the static deps listed in deps, they are static entries.
It is an error if the same file is listed several times as a static entry.
The second case is when a file is both a dep and a target. You may have noticed that the definition above does not preclude this case, mostly because a file may start its life as a dep and become a target. This is an error unless either:
- The file is finally unlinked (or was never created).
- Its
source_okflag has been set and itsincrementalone is not.
This allows rules that alter sources to choose between handling any previous content without impact on final value (incremental) or compute value based on previous one (in which case it must be a dep). In this latter case, the job will rerun if such dep-target is actually modified.
The third case is when a target was not declared as such.
foo can be declared as target by:
- Matching a
targetsorside_targetsentry. - Calling
lmake.target('foo')(unlessallow=Falseis passed). - Executing
ltarget fooin which the-aoption is not passed.
A target that is not declared is an error.
Processing a target
Targets are normally erased before the start of the job execution, unless they are sources or flagged as incremental.
In case a target is also a dep, it is automatically flagged as incremental, whether it is an error or not.
If a job is run when a not incremental and not source target exists, it is deemed unreliable and is rerun.
Best effort
Open-lmake tries to minimize the execution of jobs, but may sometimes miss a point and execute a job superfluously. This may include erasing a file that has no associated production rule. Unless a file is a dep of no job, open-lmake may rebuild it at any time, even when not strictly necessary.
In the case open-lmake determines that a file may have actually been written manually outside its control, it fears to overwrite a valuable user-generated content. In that case, open-lmake quarantines the file under the LMAKE/quarantine dir with its original name. This quarantine mechanism, which is not necessary for open-lmake processing but is a facility for the user, is best effort. There are cases where open-lmake cannot anticipate such an overwrite.
tmp dir
The physical dir is:
- If
$TMPDIRis set to empty, there is no tmp dir. - If open-lmake is supposed to keep this dir after job execution, it is a dir under
LMAKE/tmp, determined by open-lmake (its precise value is reported bylshow -i). - Else if
$TMPDIRis specified in the environment of the job, it is used. Note that it need not be unique as open-lmake will create a unique sub-dir within it. - Else, a dir determined by open-lmake lying in the
LMAKEdir.
Unless open-lmake is instructed to keep this dir, it is erased at the end of the job execution.
At execution time:
- If
$TMPDIRis set to empty, it is suppressed from the environment and if the job uses the default tmp dir (usually/tmp), an error is generated. - Else
$TMPDIRis set so that the job can use it to access the tmp dir.