Skip to content

Commit

Permalink
mamake: add %{VAR|script} and %{VAR@script} expansions
Browse files Browse the repository at this point in the history
The Mamfiles still had one remaining human maintenance problem --
when building object files from source files in a loop (see
4eaf25c), the base names of all the object files needed to be
repeated even though those are known from the list of source files.

To make it possible to maintain a "single point of truth" for
source and object file basenames while maximising flexibility, this
commit adds two new variable expansion types to mamake:

In %{VAR|S}, the value of VAR is split into fields by whitespace
and the one-line sh(1) script S is executed with each field written
as a separate line to its standard input. The value of the
expansion is the output of S with each newline changed back into a
space, except that a terminating newline (if any) is discarded.
This mechanism allows easy editing of variable values containing
multiple pathnames using line-oriented utilities thet read from
standard input, such as sed(1) or grep(1).

In %{VAR@S}, the value of VAR is split into fields by whitespace
and each field becomes a positional parameter (starting at $1) in
the one-line sh(1) script S. The value of the expansion is the
output of S with each newline converted into a space, except that a
terminating newline (if any) is discarded. This allows easy
processing of one or more pathnames using shell commands that take
arguments, such as basename(1) or printf(1), or shell loops.

This commit applies some other tweaks to mamake as well.

src/cmd/INIT/mamake.c (main changes):
- Simplify standards macros. Tested on macOS, Linux, *BSD, Solaris.
- buffer(): Do not reinitialise the 'end' member when reusing a
  previously dropped buffer. Since its allocation is never shrunk
  after growing it, 'end' should simply be left alone.
- append(): Remove unused str==0 reset functionality.
- substitute():
  - Scan for operator characters at the second character after
    %{/${ instead of the first. This simplifies things.
  - Recognise | and @ as operators for new %{...} expansions.
  - case '@': Added. Split fields into argv[] by whitespace. The
    script execution and output reading code is shared with '|'.
  - case '|': Added. Write value to temp file, converting
    whitespace to newlines. Execute script using our chosen shell
    and I/O redirected. Read output back, converting each newline
    but the last to a space.
- execute(): Change to execute_v() which takes an additional list
  of arguments in argv; this is to support %{VAR@S}. Reimplement
  execute() as a special case of execute_v().
- make():
  - Instead of expanding variables in specific arguments as needed,
    expand all variables in the entire line in one go, before
    separating the command name, argument word and operand string.
    This is necessary becasue expansions may now contain spaces,
    which broke the previous way. The entire mamake program now
    contains only one expand() call.
  - 'bind' command: It is a botch for 'bind' to potentially affect
    the automatic variables. Save the automatic variables before
    including generated dependency rules and restore them after.
  - 'make' command: No longer wait for all background jobs to
    finish before executing a shell action. This was not needed and
    did harm, as it stalled parallel builds. From now on, virtual
    rules are never run in parallel with *subsequent* shell actions
    (this is needed for recursion shell actions to work correctly)
    but previous shell actions may still run in parallel with it.

src/cmd/INIT/Mamfile,
src/lib/libcmd/Mamfile,
src/lib/libdll/Mamfile,
src/cmd/ksh93/Mamfile:
- Refactor to implement the simplification made possible by the new
  features. C source file declarations are enclosed in a virtual
  rule so all their names (automatically saved up in %{^}) can be
  copied to a new variable and used with one of the new expansion
  types later.
- In ksh93/Mamfile, this even allows collapsing four compile loops
  in one, as we can now deal with .c files in four different
  subdirectories using a basename(1) invocation.

src/lib/libast/Mamfile:
- No changes. The dependencies and compiler invocations are too
  messy to have everything compile in a loop with identical
  compiler invocations. Further refactoring may be possible, but
  needs to be done carefully and that is for a separate commit.
- Remove an outdated 'slow' comment (re: 6b66c18).

src/cmd/INIT/utils/Mamfile_rm_unused_vars:
- Fix to work properly with special expansions and new-style %{...}
  expansions.
  • Loading branch information
McDutchie committed Aug 24, 2024
1 parent 958db76 commit f06dffd
Show file tree
Hide file tree
Showing 8 changed files with 962 additions and 883 deletions.
131 changes: 61 additions & 70 deletions src/cmd/INIT/Mamfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ make install virtual
note * install helper scripts
note *

make %{INSTALLROOT}/bin/probe
make helper_scripts virtual
loop SCRIPT iffe mktest regress crossexec mkreq mkreq-maplib mprobe proto dylink mkdeps
make %{SCRIPT}
makp %{@}.sh
exec - cp %{<} %{@} && chmod u+w,+x %{@}
done
done
make probe
make probe.sh
makp C+probe
Expand All @@ -30,17 +36,15 @@ make install virtual
done
exec - cp %{<} %{@} && chmod u+w,+x %{@}
done
exec - cp -f %{<} %{@}
done
loop SCRIPT iffe mktest regress crossexec mkreq mkreq-maplib mprobe proto dylink mkdeps
make %{INSTALLROOT}/bin/%{SCRIPT}
make %{SCRIPT}
makp %{@}.sh
exec - cp %{<} %{@} && chmod u+w,+x %{@}
note * pre-install this lot
loop SCRIPT %{^}
make %{INSTALLROOT}/bin/%{SCRIPT}
prev %{SCRIPT}
exec - cp -f %{<} %{@}
done
exec - cp -f %{<} %{@}
done
done

make %{INSTALLROOT}/bin/mamprobe
note * the probe.sh dependency is needed for mamake to redo the probe when C+probe or make.probe is edited
prev probe.sh
Expand Down Expand Up @@ -76,55 +80,43 @@ make install virtual

setv CC cc

note *
note * check if -ldl is required
note *
note * NOTE: this works around the sgi botch:
note * (1) irix 5.* made -ldl optional but warned
note * (2) irix 6.* has no -ldl
note * (3) dynamic progs built on irix 5.* and using -ldl fail
note * at runtime on irix 6.* because -ldl is not there
note *
make %{INSTALLROOT}/lib/lib/dl
make req_files virtual
note *
note * check if -ldl is required
note *
note * NOTE: this works around the sgi botch:
note * (1) irix 5.* made -ldl optional but warned
note * (2) irix 6.* has no -ldl
note * (3) dynamic progs built on irix 5.* and using -ldl fail
note * at runtime on irix 6.* because -ldl is not there
note *
make dl.req
makp dl.c
exec - mkreq-maplib %{CC} : dl : %{^} : dl
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
note *
note * requiring these is a botch
note *
make %{INSTALLROOT}/lib/lib/iconv
note *
note * requiring these is a botch
note *
make iconv.req
makp iconv.c
exec - mkreq-maplib %{CC} : iconv : %{^} : iconv
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
make %{INSTALLROOT}/lib/lib/w
make w.req
makp w.c
makp w2.c
exec - mkreq-maplib %{CC} : w : %{^} : w
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
note *
note * miscellaneous -l* checks
note *
make %{INSTALLROOT}/lib/lib/intl
note *
note * miscellaneous -l* checks
note *
make intl.req
makp intl.c
exec - mkreq-maplib %{CC} : intl : %{^} : intl
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
make %{INSTALLROOT}/lib/lib/m
make m.req
makp m.c
makp m2.c
Expand All @@ -135,44 +127,35 @@ make install virtual
exec - mkreq-maplib %{CC} : m : %{^} : m
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
make %{INSTALLROOT}/lib/lib/nsl
make nsl.req
makp nsl.c
exec - mkreq-maplib %{CC} : nsl : %{^} : nsl
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
note *
note * what was sco smoking
note * almost all of gethost* are in -lnsl except gethostbyname which
note * is in -lsocket which isn't needed to resolve socket() but seems
note * to do the -lnsl job
note *
make %{INSTALLROOT}/lib/lib/socket
note *
note * what was sco smoking
note * almost all of gethost* are in -lnsl except gethostbyname which
note * is in -lsocket which isn't needed to resolve socket() but seems
note * to do the -lnsl job
note *
make socket.req
makp socket.c
prev nsl.c
exec - mkreq-maplib %{CC} : socket : %{^} : socket
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
note *
note * more substance abuse
note * gdbm's ndbm "compatibility" doesn't supply <ndbm.h>, instead supplies
note * <gdbm/ndbm.h> which provides K&R prototypes *and* it requires -lgdbm
note * some <ndbm.h> implementations use -lndbm, others -ldbm, still others -lc
note * this is why unix is starting to look like windows
note * this map allows makefiles to use -ldbm on all systems
note *
note * and this just in: sometimes its <gdbm-ndbm.h> and possibly -lgdbm_compat
note *
note * at least the -l* buck stops here
note *
make %{INSTALLROOT}/lib/lib/dbm
note *
note * more substance abuse
note * gdbm's ndbm "compatibility" doesn't supply <ndbm.h>, instead supplies
note * <gdbm/ndbm.h> which provides K&R prototypes *and* it requires -lgdbm
note * some <ndbm.h> implementations use -lndbm, others -ldbm, still others -lc
note * this is why unix is starting to look like windows
note * this map allows makefiles to use -ldbm on all systems
note *
note * and this just in: sometimes its <gdbm-ndbm.h> and possibly -lgdbm_compat
note *
note * at least the -l* buck stops here
note *
make dbm.req
makp db.c
makp gdbm.c
Expand All @@ -181,18 +164,26 @@ make install virtual
exec - mkreq-maplib %{CC} : dbm : %{^} : db gdbm_compat gdbm ndbm dbm
prev mkreq-maplib
done
exec - cp -f %{<} %{@}
done
note *
note * make these available to 'bind'
note *
loop R %{^|sed 's/\.req$//'}
make %{INSTALLROOT}/lib/lib/%{R}
prev %{R}.req
exec - cp -f %{<} %{@}
done
done
done req_files
done install

make test dontcare virtual
make test.iffe virtual
makp iffe.tst
exec - regress iffe.tst iffe
done
make test.mamake virtual
makp mamake.tst
exec - : testing non-libast mamake at $PWD/mamake :
exec - regress mamake.tst mamake
done
make test.iffe virtual
makp iffe.tst
exec - regress iffe.tst iffe
done
done test
34 changes: 30 additions & 4 deletions src/cmd/INIT/README-mamake.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,35 @@ The last `?` is optional.

In `%{`*variable*`-`*x*`}`, the value of *variable* is substituted
if it is defined and non-empty, otherwise *x* is substituted.
This expansion type is disabled for *variable* names starting with `-`.
In `%{`*variable*`+`*x*`}`, *x* is substituted if the value of
*variable* is defined and non-empty, otherwise the reference is removed.
In both cases, *x* may be empty.
Note that, unlike in `sh`(1), no distinction is made between an undefined
variable and a defined variable with an empty value.

In `%{`*variable*`|`*s*`}`, the value of *variable* is split into fields by
whitespace and the one-line `sh`(1) script *s* is executed with each field
written as a separate line to its standard input. The value of the expansion
is the output of *s* with each newline changed back into a space, except
that a terminating newline (if any) is discarded.
This mechanism allows easy editing of variable values containing multiple
pathnames using line-oriented utilities thet read from standard input,
such as `sed`(1) or `grep`(1).
For example, if the automatic variable `%{^}` (see below) contains `foo.c
bar.c baz.c`, then `%{^|sed 's/\.c$//'}` yields `foo bar baz`.

In `%{`*variable*`@`*s*`}`, the value of *variable* is split into fields by
whitespace and each field becomes a positional parameter (starting at `$1`)
in the one-line `sh`(1) script *s*. The value of the expansion is the output
of *s* with each newline converted into a space, except that a terminating
newline (if any) is discarded.
This allows easy processing of one or more pathnames using shell commands
that take arguments, such as `basename`(1) or `printf`(1), or shell loops.
For example, if the value of `SRC` is `/dir/path/foo.c`, then
`%{SRC@basenanme -- "$1" .c}.o` yields `foo.o`
(any subsequent fields would be discarded in this case).

### Automatic variables ###

The following variables are set and updated automatically.
Expand Down Expand Up @@ -214,8 +237,8 @@ The following *attribute*s are available:
* `notrace`: Disables echoing (xtrace) of shell action commands.
This does not disable the trace header for the containing rule (see *Shell actions* below).
* `virtual`: Marks a rule that is not associated with any file.
The associated shell action is executed every time the rule is processed.
It will not run in parallel with other jobs even if the `-j` option was given.
Its associated shell action (if any) is executed every time the rule is processed.
It will not run in parallel with subsequent shell actions even if the `-j` option was given.
By convention, a virtual rule with target `install` performs pre-installation.

> *Obsolete:*
Expand Down Expand Up @@ -437,7 +460,6 @@ Cross-directory dependencies on AST library headers (preinstalled in
`$INSTALLROOT/include/ast`) are similarly communicated via a file with
the path `%{INSTALLROOT}/lib/mam/`*libraryname*.
The `bind` command automatically includes this file as necessary.
Note that this may have side effects on the automatic variables.
The `INIT` package preinstalls the `mkdeps` script that Mamfile shell
actions should use to generate this file while building each library.
The generated file is expected to:
Expand Down Expand Up @@ -484,7 +506,7 @@ Each `make`…`done` command containing a shell action (i.e., one or more
`mamake` continuing to process subsequent shell actions at the same or
deeper nesting levels before the current one has finished.
There is one exception to this: shell actions belonging to rules with the
attribute `virtual` never run in parallel (although their subrules will).
attribute `virtual` will not run in parallel with subsequent shell actions.

Each `done` command corresponding to a `make` rule will block any further
reading of the Mamfile until all shell actions belonging to rules nested
Expand Down Expand Up @@ -532,6 +554,10 @@ maintain Mamfiles by hand. The following lists the important changes.
and confusion with the shell's `${` syntax in shell actions.
* The `makp` command may be used instead of an empty `make``done`
block to declare a simple prerequisite with optional attributes.
* The special expansion syntaxes `%{`*variable*`|`*sh-script*`}` and
`%{`*variable*`@`*sh-script*`}` have been added, allowing the editing of
variable value fields written to the script's standard input as lines or
passed as positional parameters.
* **At strict level 1 and up:**
* Appending attributes to `done` instead of `make` is deprecated
and produces a warning.
Expand Down
Loading

0 comments on commit f06dffd

Please sign in to comment.