SMAKE(1)
bestie π welcome to SMAKE(1) from SchilyΒ΄s USER COMMANDS β okay so this is the manual for smake, and itβs lowkey one of those tools thatβs doing the absolute most in the best way. No cap, itβs giving βmake but with main character energy.β Letβs go. π¦
π NAME π
smake β maintain, update, and regenerate programs. periodt. π
π¦ SYNOPSIS π¦
smake [ options ] [ -f makefilename ] [ target1...targetn ]
πΈ DESCRIPTION πΈ
Okay so Smake executes command sequences based on relations of modification dates of files. The command sequences are taken from a set of rules found in a makefile or in the set of implicit rules. The argument target is typically a program that is to be built from the known rules.
If no -f option is present, smake looks for SMakefile then for Makefile and then for makefile in the named order. If in POSIX mode, smake looks for makefile, Makefile and then for SMakefile.
If no target is specified on the command line, smake uses the first regular target that could be found in makefilename and that does not start with a dot ('.') letter combination.
If a target has no explicit entry in the makefile, smake tries to use implicit rules or the .DEFAULT rule.
Unlike most other make programs, smake propagates all command line macros to sub makes. This is a big advantage with hierarchical makefile systems. Propagation is done in a POSIX compliant way using the MAKEFLAGS= environment.
Unlike other make programs, smake includes a set of automake features that allow one to implement portable, layered, object oriented makefiles. iconic behavior, honestly. π
β¨ OPTIONS β¨
okay bestie, hereβs the full option menu β no skips, no βoops I forgot one,β weβre covering every single flag:
-aβ Do not set up architecture specific make macros likeMAKE_ARCH,MAKE_OSand similar that are provided as part of the automake features.-C dirβ Change the working directory todirbefore starting work. This option may be specified more than once, each subsequent occurrence of the option is dependent on those that came before it.-eβ Environment variables override macro definition in Makefile(s).-iβ Ignore error codes returned by commands that are called from rules. This is equivalent to having the special target.IGNOREwithout dependencies inside a makefile.-j maxjobsβ Specifies the maximum total number of jobs that are run in parallel. This option is currently ignored. (Yes, bestie, itβs literally just vibing there. π)-kβ Ignore errors by aborting work on the current target and continuing the work on other targets that do not depend on the failed target.-Nβ Continue if a named dependency does not exist and no related source could be found.Smakeby default aborts on this condition, but it seems that traditional UNIX make programs ignore this condition for unknown reason.-nβ Donβt make β only say what to do. This prints the commands that would be executed on standard output. Lines with a plus sign ('+') will be executed in any case. Even those lines that have an at sign ('@') will be printed to standard output. If a command line contains the macro$(MAKE)or${MAKE}this command line is executed even if there is no plus sign ('+').-pβ Print the complete set of macro and target definitions on standard output.-qβ Question mode. Exit code is0if the target is up to date. In all other cases, the exit code is1. Lines with a plus sign ('+') will be executed in any case.-rβ Turn off internal rules. Do not use the built in rules and do not read the files./defaults.smkand/opt/schily/share/lib/smake/defaults.smk.-Sβ Undo the effect of the-koption and terminatesmakeif an error occurs while updating targets.-sβ Be silent. (Do not print command lines or touch messages on standard output before they are executed.) This is equivalent to having the special target.SILENTwithout dependencies inside a makefile.-tβ Touch objects instead of executing the defined commands. This brings a target up to date by simply setting the modification time of the targets instead of performing their rules. Warning: This can be dangerous when files are maintained by more than one person. Targets that do not have a command associated are not touched. The command lines that are associated with a target are not executed. Lines with a plus sign ('+') will be executed in any case.-wβ Donβt print warning messages.-Wβ Print extra debug messages and warning messages.-WWβ Print even more extra debug messages and warning messages.-Dβ Display Makefiles as they are read in. This allows one to debug the effective makefile when the makefiles use include statements.-DDβ Display Makefiles and internal rules as they are read in. This is the same as specifying-Dbutsmakestarts to print the effective makefile when reading the internal definitions. If-DDDis specified, the printout includes even the definitions of the environment variables as make macros.-dβ Print the reason why a target has to be rebuilt. Also turns off external and internal silent flag. This special feature allows one to write makefiles that usually suppress the printing of command lines (using a'@'at the start of a command line) as in debug modesmakeundoes the effect of the'@'character.-ddβ Print additional debugging messages. This includes the messages from the-dflag and additional messages that allow execution tracing of the various rules. If more'd'characters are added (e.g.-dddd) the verbose level for tracing is enhanced.-xMβ Print the make file include dependency list. This option allows one to trace complex makefiles that make heavy use of the include statement.-xdβ Print extended debug messages. This is only useful, if you have internal knowledge tosmakeitself and want to do heavy debugging.-probjβ Print the whole object tree. This is only useful, if you have internal knowledge tosmakeitself and want to do heavy debugging.-helpβ Prints a short summary of thesmakeoptions and exits.-versionβ Prints thesmakeversion number string and exits.-posixβ Forcesmaketo go into POSIX mode. This is equivalent to having the special target.POSIXinside a makefile. See the description of the special target.POSIXfor more information.-f makefilename,mf=makefilename,mf= makefilenameβ Specifies the Makefile.macro=valueβ Set a macro. A macro definition on command line overrides any other macro definition.
Options, command line macros and target name may be intermixed. Smake will always first evaluate all options then all command line macros and then will start building the targets. With smake it is not possible to first make one target, then set a command line macro and then make the next target with different macro values, POSIX does not specify the behavior in this case anyway.
π OPERANDS π
The following operands are supported:
targetβ any number of target names. The targets are built in the order of occurrence.macro=valueβ A macro definition. Macro definitions from the command line overwrite regular macro definitions in makefiles or taken from the environment.macro::=valueβ A macro definition. The value is expanded before the assignment is done. This variant of a macro definition creates a different macro type that is not expanded on use. The::=operator is only supported when in POSIX mode.macro:::=valueβ A macro definition. The value is expanded before the assignment is done.macro +=valueβ Append to a macro definition. A space is required before+=and the whole command line macro definition needs to be quoted.macro +:=valueβ Append to a macro definition. The value is expanded before the assignment is done. A space is required before+:=and the whole command line macro definition needs to be quoted.
All macro definitions are evaluated before any target is built. π
β¨ BASICS β¨
Okay so smake is literally the queen of keeping your files in sync β she maintains the dependency of a group of files by building a dependency tree of the targets (derived files) and the files that are the prerequisites of the targets (called the sources or the dependency list of files). A makefile contains a description of the relations of the targets and the prerequisites and the commands that should be executed to make the target up to date. π
foo : main.o lex.o parse.o
main.o: main.c main.h
The file foo depends on the files main.o lex.o and parse.o while main.o depends on main.c and main.h. This example describes a project in the C-programming language, but it is possible to maintain any project that depends on the modification time of the related files. π¦
Smake considers a file up to date if it has been modified after the files it depends on (its prerequisites) and all files it depends on are recursively up to date too. If a file doesnβt exist, itβs considered to be out of date. The targets are processed in the order they appear in the dependency list. β¨
For example, if main.o is newer than foo, then it is assumed that foo is not up to date. If main.c is newer than main.o, then it is assumed that both, main.o and foo are not up to date. π
Smake updates all targets based on rules. A rule specifies a target and its prerequisites and defines a set of commands that allow one to create an up to date target from its prerequisites. π
If the target and its prerequisites are named explicitly, the rule is called an explicit rule. If the target and its prerequisites are named in an abstract way, the rule is called an implicit rule or an inference rule. π
If smake is called to update a target, it first checks if an explicit rule for this target can be found. If there is no explicit rule for this target, then smake looks for an implicit rule. It checks the following methods of specifying an implicit rule until a suitable rule is found. Each method is described in section Makefile Format below or in the section Implicit Rules. π
- Pattern matching rules from a user supplied makefile.
- Suffix rules, either from a user supplied makefile or from the set of builtin rules.
- Simple suffix rules, either from a user supplied makefile or from the set of builtin rules.
- SCCS retrieval. If smake finds a more recent SCCS history file, it tries to retrieve the most recent version from the SCCS history. See the description of the
.SCCS_GETspecial target. - The rule from the
.DEFAULTtarget entry, if such an entry exists in a makefile.
A limited set of built in rules is compiled into smake. A more complete set is read from the file ./defaults.smk or /opt/schily/share/lib/smake/defaults.smk. πΈ
π Makefile Format π
Smake reads its rules from a file named SMakefile, Makefile or makefile in the current directory and checks for the files in the named order. If in POSIX mode, smake looks for makefile, Makefile and then for SMakefile. If a different makefile is specified with the -f option, smake reads from this file. In case that no makefile was specified with the -f option, it is not an error if no makefile exists. In this case smake only uses the built in rules. The term makefile is used for any user supplied file that contains rules for the make utility. π«Ά
A makefile contains rules, macro definitions, special make directives and comments. A rule may either be a target rule (explicit rule) or an implicit rule. Smake itself contains or loads a number of built in implicit rules which are not used if the -r option is used. The user specified makefile defines additional explicit and implicit rules and macros. If a macro is defined more than once, the last definition is used. If a target rule is defined more than once, the dependency list is the sum of all dependency lists and the set of commands is the set of commands from the last specification. A back slash ('\') at the end of a line indicates that this line is to be continued on the next line. An escaped newline is replaced by a single space character and the white space at the beginning of the next line is removed. A comment starts with a hash mark ('#') and ends if an un-escaped new line is found. π
π οΈ Command Execution π οΈ
Command lines associated with rules are sequentially executed line by line in an own process or shell. All commands may use constructs supported by the standard shell (/bin/sh). POSIX shell constructs are permitted as long as the local shell supports them. Command lines may have prefixes that are interpreted by smake before the rest of the command line is executed or passed to the shell. All characters from the list below that immediately follow the TAB character are interpreted by smake. The first non-blank character that is not from the list below is the first character passed to the shell. The following prefixes are implemented: π
-β If the character'-'is amongst the prefix characters or if the-ioption has been specified or the special target.IGNOREhas been specified with no dependencies or with the current target in the list of dependencies, the exit code from the command line is ignored.+β If the character'+'is amongst the prefix characters, the command line is executed even if one of the options-n,-qor-twas specified.@β If the character'@'is amongst the prefix characters or if the-soption has been specified or the special target.SILENThas been specified with no dependencies or with the current target in the list of dependencies, the command line is not printed before it is executed.?β Reserved for future use (currently ignored).!β Reserved for future use (currently ignored).
Typical UNIX systems are fast with calling the shell. Some operating systems (like e.g. win32) however are slow with creating processes. As calling a command via the shell results in creating an additional process, command execution via the shell causes unneeded overhead for simple commands. To speed up command execution, smake tries to avoid calling commands via the shell when no shell meta character appears on the commandline. As commandlines used in makefiles frequently look like: β
@echo message; cc ...
smake avoids to call a shell in such cases by executing simple echo commands inline in case they are at the beginning of a command. A simple echo command is a command without I/O redirection and without shell variable expansion. π§
If the environment FORCE_SHELL is present or the special target .FORCE_SHELL: has been defined, smake does not try to optimize command execution and calls all commands via the shell. If the SHELL special macro is used to define an alternate shell that is based on a different set of shell meta characters than the meta characters used by the POSIX shell:
# | = ^ ( ) ; & < > * ? [ ] : $ ` ' " \ \n
it is recommended to define the special target .FORCE_SHELL: in addition. π
π― Target rules π―
A target rule looks this way:
target ... [:|::] [dependency] ... [; command] ...
[<tab> command]
...
The first line may not start with a TAB character. It starts with a target name or a white space separated list of target names, in both cases followed by colon as a target separator (':') or by a double colon ('::'). The colon may be followed by a dependency name or a white space separated list of dependency names. The double colon allows one to specify alternate dependency lists and commands for the same target. The dependency list may be followed by a semicolon (';') and a Bourne shell command. There may be additional lines with Bourne shell commands, all starting with a TAB. The first line that does not start with a TAB starts another definition. π₯
Bourne shell commands may be continued over more than one line if the new line is escaped with a back slash. The next line must also start with a TAB character. π
π Make directives π
export macro ...β The list of macros that follows theexportdirective is exported in the list of environment variables. If an environment variable with the same name already exists, its value is replaced by the current value of the related make macro. An empty list of macro names is not considered an error. If thePATHmacro is in the list of parameters, the path search of the current smake process is also affected. If theSHELLmacro is in the list of parameters, it is ignored and the previousSHELLenvironment is kept. πinclude filename ...β Reading and parsing makefiles is temporarily continued with the list of the file name parameters to theincludedirective. If one or more file names from the parameter list contains make macro expressions, the macro expressions are evaluated before the file names are used. A single make macro may contain a list of include file names. If smake knows rules to make the files to include, smake will evaluate the related rules before doing the include. Include directives may be nested as long as there are unused file descriptors. An empty list of filenames is not considered an error. If one or more files from the parameter list do not exist, smake aborts. π-include filename ...β The-includedirective is implemented similar to theincludedirective, except that a nonexistent file is not considered to be an error. πΈreadonly macro ...β Mark the list of macros in the parameter list as readonly. This prevents further modification of the content of the related macros. An empty list of macro names is not considered an error. πunexport macro ...β The list of macros that follows theunexportdirective is removed from the list of environment variables. An empty list of macro names is not considered an error. If thePATHmacro is in the list of parameters, the path search of the current smake process is also affected. If theSHELLmacro is in the list of parameters, it is ignored and the previousSHELLenvironment is kept. π¦
β¨ Automake Features β¨
okay so bestie, smake literally implements automake features that help you write highly portable makefile systems π . The automake features are implemented with a set of special purpose macros and by special rules that help extend the functionality and automated adaptivity of smake. Itβs giving βportable makefile system, but make it adaptiveβ fr fr.
πΈ Automake special purpose macros πΈ
The special purpose macros that have names that match MAKE_* (see section Special Macros) are part of the automake features of smake. The related special macros are set up by smake to contain values that have been retrieved from the results of the uname(2), sysinfo(2) or sysctl(2) system calls. The values for these macros may however be overwritten from a makefile or from a command line macro definition. So yes, smake fills them in, but you can absolutely take the wheel π«Ά.
π― Automake special targets π―
The special target .INCLUDE_FAILED allows one to define a rule that is evaluated in case that an include file does not exist and could not be made from other rules. As the associated command for this target is typically a shell script, the shell script can be written to create fall back rules in a platform independent way. Thatβs lowkey iconic: no include file? boom, fallback rules, platform independent, slay π.
π Search Rules For Files π
In many cases, it is desirable to hold object files in a special directory which is different from the directory where the source files are located. For this reason, smake allows one to specify a directory where all targets are placed in case they are a result of an implicit rule. Add
.OBJDIR: object_directory
to the makefile to activate this smake feature. If a makefile uses this feature, it must either explicitly use the right file names (including the object directory) or use dynamic macros that are automatically updated to include the path of the object directory. If smake uses file name searching, the dynamic macros $<, $0, $n, $r1, $^, and $? are updated to reflect the actual path of the source or object directory. The way those dynamic macros update themselves? Obsessed πβ¨.
A line of the form:
.SEARCHLIST: source_directory_1 object_directory_1 ...
that contains pairs of source and corresponding object directory names, will cause smake not only to search for files in the current directory and in the directory which is pointed to by .OBJDIR, but also in the directories from .SEARCHLIST:. Smake first looks in the current directory, then in the directory pointed to by .OBJDIR and then in the directories from .SEARCHLIST:. Order matters, bestie β current dir first, then .OBJDIR, then the .SEARCHLIST: directories π
.
If a source could be found in a specific source directory of the list of source and object directories, the targets will only be looked for in the corresponding object directory. A result of a source, which is found in any of the source-directories of the .SEARCHLIST: is placed into the corresponding object-directory. Matching source dir β object dir, no cap π§.
A line in the form:
.OBJSEARCH: value
Where value may be one of src, obj, all. causes smake to look for left-hand-sides of rules in only the source-directories, the object-directories or both. The default value for .OBJSEARCH: is: all. That causes smake to search for results in object-directories and source-directories. So by default itβs looking in both places, main character energy π₯.
If no .SEARCHLIST target exists, but a VPATH= macro is found, smake transforms the content of the VPATH= macro into a form suitable for .SEARCHLIST:. This is done by putting each VPATH= entry twice into the .SEARCHLIST:. Please report problems with this solution. Like fr fr, if that solution acts up, report it π.
π Macros π
π Macro Definitions π
Macros are defined by a line in the form:
macroname= value
The value assigned to the macro contains all characters after the equal sign up to a comment character ('#') or an unescaped newline. Any blank characters that directly follow the equal sign are ignored. So the value starts after the = and stops at a comment or unescaped newline, and the blanks right after =? ignored, periodt π
.
An alternate macro definition uses the form:
macroname += value
to append value to the current definition for macroname. Note that there needs to be white space before the += as smake allows macro names like C++. Yes bestie, C++ is a whole macro name vibe, so whitespace before += is not optional π«Ά.
β οΈ Warning: Macros created with the::=operator will cause the+=operator to evaluate macros on the right side of the operator before doing the actual append operation. To avoid unpredictable behavior, it is recommended to use at least one lower case character in the name of macros that have been created using the::=operator.
An alternate macro definition uses the form:
macroname +:= value
to append value to the current definition for macroname after value has been evaluated. The string $$ is not expanded with the +:= operator, it is left unmodified while the right side is expanded. Note that there needs to be white space before the += as smake allows macro names like C++. The $$ staying unmodified? Thatβs the detail that matters, donβt sleep on it π.
A macro definition that uses the form:
macroname ?= value
will assign value to the current definition for macroname in case that macroname does not yet have a definition. So itβs like βonly if not defined yet,β no overstepping π.
A macro definition that uses the form:
macroname:= value
will evaluate the right side of the assignment when the line is read by the parser. The := operator has been introduced in 2005 as a temporary feature. It currently is an alias to the final :::= operator explained below.
Note that SunPro make was the first make implementation that introduced := in 1986 already, but for a different incompatible purpose, GNU make in 1990 introduced := with another different incompatible purpose that later has been standardized by POSIX issue 8 as the ::= operator. BSD make implements the same behavior as smake for the := assignment operator. A makefile that uses := thus is non-portable for use with different make implementations. smake may change its behavior in the future and implement a SunPro Make compatible conditional macro assignment instead of the current behavior for :=. For this reason, it is recommended to use :::= instead of :=. Thatβs a whole lore drop, but the takeaway is clear: use :::= for new makefiles, bestie πΈ.
A macro definition that uses the form:
macroname ::= value
will evaluate the right side of the assignment when the line is read by the parser. Macros created with this operator are not expanded on use. Note that this is the POSIX variant of the GNU immediate assignment that is portable to make implementations that are compatible to POSIX issue 8 and used in POSIX mode. The ::= operator is only supported by smake when in POSIX mode. POSIX mode only, iykyk π.
β οΈ Warning: This operator may cause unpredictable behavior, since it creates a different type of macro that is not expanded on use and that causes a different behavior with the+=operator. In order to avoid the unpredictable behavior with the+=operator, it is recommended to avoid the::=operator in favor of:::, or to use at least one lower case letter as a marker in the names of all macros that have been created with the::=operator.
A macro definition that uses the form:
macroname :::= value
will evaluate the right side of the assignment when the line is read by the parser. The string $$ is not expanded with the :::= operator, it is left unmodified while the right side is expanded. Note that this is a naming for the immediate expansion assignment operator that is usable in portable makefiles with make implementations compatible to POSIX issue 8. The operator :::= should be used in favor of := and ::= for all new makefiles. New makefiles? :::= is the moment, no cap π
β¨.
A macro definition that uses the form:
macroname:sh= command
will call command and define the macro value to be the output of command when the line is read by the parser. Shell command in, output out, macro defined when the parser reads the line β iconic behavior fr fr π.
π¦ Macro References π¦
Macros may be referenced by either: $(macroname) or ${macroname}. The parentheses or braces are optional for a macro with a single character name. Single character macro? You can keep it cute and skip the parentheses/braces π.
Macro definitions and macro references can appear anywhere in the makefile. Macro references are expanded under the following circumstances:
- Macros in target lines are evaluated when the target line is read by the parser.
- Macros in command lines for rules are evaluated when the command is executed.
- Macros in the string before
':','::','='or'+='(names for target or macro definitions) are evaluated when the target line is read by the parser. - Macro references after the equal sign in a macro definition are not evaluated until the defined macro itself is used in a rule or a command or to the left of the equal sign from a macro definition.
The timing of when macros expand is literally the whole plot, bestie π. Target line? parser time. Command line? command execution time. Names before ':', '::', '=' or '+='? parser time. Right side of a macro definition? not until the macro is actually used in a rule or command, or to the left of the equal sign from a macro definition. Keep that in your notes π«Ά.
π Macro Substitution π
Macro references may be used to substitute macro values. There are different methods for macro substitution. Different methods, same cute macro reference energy β¨.
π Suffix Replacement Macro References π
A macro reference in the following form:
$(name:subst1=subst2)
is used to replace the suffix or word subst1 in the value of the macro name by subst2. Words are separated by space or tab characters. Suffix or word replacement, word by word, spaces and tabs doing the separating π
.
π Pattern Replacement Macro References π
A macro reference in the following form:
$(name:op%os=np%ns)
is used to replace prefixes and suffixes in words. In this case, op is the old prefix, os is the old suffix, np is the new prefix and ns is the new suffix. The strings op, os, np and ns may all be empty strings. The pattern % matches a string of zero or more characters. The matched pattern is carried forward to the replacement target. For example:
OBJECT=foo.o
SCCS_HISTFILE=$(OBJECT:%.o=SCCS/s.%.c)
replaces foo.o by SCCS/s.foo.c when the macro $(SCCS_HISTFILE) is referenced. The replacement is done on a word by word base and the white space between words is literally retained. If there is no match, the result is unmodified. So if it matches, it matches and carries the pattern forward; if not, it leaves your result alone. No forced glow-up π.
π§ Shell Replacement Macro References π§
A macro reference in the following form:
$(name:sh)
will interpret the content of the macro name as a shell command line, call the shell with the content of the macro and return the output of the called command. Shell command goes in, output comes back, macro reference gets the result. Thatβs the whole vibe π₯.
β¨ Special Targets β¨
These are the special-guest-star targets, bestie. They're not files you build β they're instructions to smake itself. Iconic behavior, honestly π
.DEFAULT:β if a target looks out of date and literally no other rule applies to it,smakeruns the commands from this special target. Plot twist: the.DEFAULTtarget may not have a dependency list. Periodt..DONE:β if this one's present,smakeruns its commands after all targets have been processed. It also gets executed if a failure happens and there's no.FAILEDtarget hanging around. And yeah β the.DONEtarget may not have a dependency list either. π.FAILED:β if this is present and an error happened,smakeruns these commands instead of the commands of the.DONEtarget, after all targets have been processed. Same rule as its siblings: the.FAILEDtarget may not have a dependency list. π.FORCE_SHELL:β if present,smakeruns all commands via the shell instead of trying to optimize command execution for simple commands. No shortcuts, we do the full thing. β¨.GET_POSIX:β reserved for future use. Nothing to see here, babe. π.IGNORE:β the "errors? never heard of her" target.- If it's present with no dependencies,
smakeignores errors from commands..IGNORE:without dependencies is exactly equivalent to using the-ioption. - If
.IGNORE:has dependencies and the current target is in that dependency list, then the exit code for the related commands is ignored. Selective ignoring. Very main character. π
- If it's present with no dependencies,
.INCLUDE_FAILED:β this one implements automake features for object oriented layered makefiles. If the target is present and defines commands,smakeruns those commands when a makefile could not be included and there was no other explicit or implicit rule that did create the missing make include file. π«Ά
The .INCLUDE_FAILED target may not have a dependency list. When the commands for .INCLUDE_FAILED get called, the dependency list of the special target is set up to the include filename that caused the failure. So it's highly recommended to define those commands to include $^ as a parameter. Heads up though: the commands for .INCLUDE_FAILED are called only for the include directive, not for -include. And if the commands can't create a file that's going to be included, smake fails. π
.INIT:β if this target is present, the target and its dependencies are built before any other target is made. First in line, always. π.KEEP_STATE:β reserved by SunPRO make. Do not use this target, to avoid problems with the SCHILY (Sing) makefile system. Consider this your warning, bestie. β οΈ.KEEP_STATE_FILE:β also reserved by SunPRO make. Also do not use it, for the exact same SCHILY (Sing) makefile system reasons. β¨.MAKE_VERSION:β reserved for future use. Future versions ofsmakemay implement this special target in a way similar to SunPRO make.
A special target shaped like this:
.MAKE_VERSION: smake-number
forces a check of the version of smake. If the version of smake differs from the version in the dependency list, smake issues a warning message. FYI the actual version of smake is smake-1.0. π
.NO_PARALLEL:β reserved for future use. Chill for now. π΄.NO_WARN:β if present and it has a dependency list, specific warnings may be disabled, depending on the names used in the pseudo dependency list.
If .NO_WARN: does not have any dependencies, the list of warning exceptions is cleared. Clean slate, we start fresh. π§
These are the pseudo dependencies that are implemented:
:=β suppress warnings about nonportable:=macro assignments.$$*β suppress warnings for using the dynamic macro'$*'in explicit rules.$$<β suppress warnings for using the dynamic macro'$<'in explicit rules.
.OBJDIR:β if this special target is present,smakeassumes every file created by a rule should go in a special directory called the object directory. That object directory is the only member of the dependency list of the.OBJDIRtarget.smakemoves the targets into that directory automatically, and automatic macros like$^,$?,$r1get modified automatically so the object directory stays transparent to you. If.OBJDIR:isn't specified, it's assumed to be the current directory'.'. π¦.OBJSEARCH:β this target may hold one of three predefined values:src,objandall. It defines where objects (targets of a rule) are searched for. The default for.OBJSEARCH:is to look for targets in both (source and object) directories or directory lists. See also.SEARCHLISTfor the whole story on thesrcandobjdirectories. π.PARALLEL:β reserved for future use. Soonβ’. β.POSIX:β if this special target is found, POSIX mode is enabled.
With this option, the verbose command lines written to the stdout stream are prefixed by a TAB character instead of three dots (...). In POSIX mode, smake also calls commands via /bin/sh -c 'cmd' instead of /bin/sh -ce 'cmd'. Which means β and this matters β smake will not stop at failing sub commands in complex commands. So if you're using the .POSIX: special target, it is highly recommended to also specify MAKE_SHELL_FLAG=-ce to make sure the make process still stops at failed commands. Don't skip that part, bestie, fr fr. π¨
.PRECIOUS:β this macro holds a list of targets that should not be removed while they're being built, ifsmakereceives a signal. If the list is empty, this applies to all targets. Precious indeed. π.PHONY:β this macro holds a list of targets that should not be checked against existing files. A target marked.PHONYwill always be considered out of date. Ifsmakereceives a signal, targets marked as.PHONYare not removed. And ifsmakeis called with the-t(touch) option,.PHONYtargets are not touched either. Fake it till you make it, literally. π.SCCS_GET:β reserved for future use. π.SCCS_GET_POSIX:β reserved for future use too. π.SEARCHLIST:β.SEARCHLISTis a list of alternate source and object directories wheresmakeshould look for targets. This macro can be used as an extended replacement for theVPATH=macro of other make programs. It holds a list ofsrcdir/objdirpairs, and the complete internal list is built from'.',.OBJDIRand the content of.SEARCHLIST. π.SILENT:β run silently, shhh. π€«- When this special target is present and has no dependencies,
smakewill not echo commands before executing them..SILENT:without dependencies is equivalent to using the-soption. - If
.SILENT:has dependencies and the current target is in the list of dependencies of.SILENT:, the related commands are not echoed before they're executed.
- When this special target is present and has no dependencies,
.SPACE_IN_NAMES:β if this target is present and has a dependency list, escaped spaces may occur in object and dependency names. If.SPACE_IN_NAMES:is not followed by any dependency, the previous dependencies are cleared and makefile parsing is done again in a POSIX compliant way.
This little code fragment lets you use the non-standard space handling for one target only:
.SPACE_IN_NAMES: true
target\ with\ spaces: dependency\ with\ spaces
command list
.SPACE_IN_NAMES:
.SSUFFIX_RULES:β if this special macro is present with no dependencies, all current Simple Suffix Rules are cleared. Wipe the slate, bestie. π.SUFFIXES:β dependencies of.SUFFIXESare appended to the list of known suffixes and get used together with the suffix rules (see Suffix Rules). If.SUFFIXESdoesn't have any dependencies, the list of known suffixes is cleared. And heads up β if the list of.SUFFIXESwas cleared, no implicit suffix rule search is done. Like, at all. π.SYM_LINK_TO:β reserved for future use. π¦.WAIT:β reserved for future use. Patience, babe. β³
β¨ Special Macros β¨
Okay bestie, these are the special macros smake keeps in its little curated folder π theyβre built in, theyβre iconic, and some of them are literally doing automake magic behind the scenes πͺ
CURDIRβ after smake has processed all-Coptions (if any), it sets the variableCURDIRto the absolute pathname of the current working directory. This value is never touched by smake again. An environment variable will not overwrite it, but a makefile may. Setting this variable has no effect on the operation of smake.MAKEβ this macro contains a path name that is sufficient to recursively call the same make program again (it either contains the last path component or the full path name of the make program). Note that this may differ from the name that was used on the command line if the name that was used on the command line would not allow one to call the same make program from a different directory using this name. For this reason$(MAKE)may not be used to check for a specific make program. Use$(MAKE_NAME)instead. If a command line contains this macro, this command line is executed even if there is no plus sign ('+').MAKEFLAGSβ this macro contains the command line flags and the command line macros smake is called with. TheMAKEFLAGSmacro is exported into the environment to allow automatic propagation of make flags and command line macros to sub make programs. The content of this macro is POSIX compliant.- If there were no command line flags or command line macros, the
MAKEFLAGSmacro is empty. - If there were only command line flags, the
MAKEFLAGSmacro contains a concatenation of the single char versions of the flags. A hyphen is the first char, soMAKEFLAGSwould be suitable as a command line flag in this case. A typical content may look this way:-id. - If there were only command line macros, the
MAKEFLAGSmacro contains a concatenation of the macro specifications. The different macro specifications are separated by a space. Any occurrence of a space or a back slash inside a macro specification is escaped by a back slash. A typical content may look this way:CC=gcc COPTX=-DTEST\ -DDEBUG. - If both command line flags and command line macros are used, the flag part is made as if there were only flags and the macro part is made as if there were only macros. The separator between the flag part and the macro part is a space, two hyphens and a space. A typical content may look this way:
-id -- CC=gcc COPTX=-DTEST\ -DDEBUG. - As the
MAKEFLAGSnotation of the command line macros uses a special escape notation to allow propagation of any possible command line macro, it is not possible to callmake $(MAKEFLAGS)from a make file.
- If there were no command line flags or command line macros, the
MAKE_MACSβ this macro contains the complete set of command line macros and the macros read from theMAKEFLAGSenvironment. The content is the same as the last part of theMAKEFLAGSmacro which holds the macro part. TheMAKE_MACSmacro is exported into the environment.MAKE_FLAGSβ this macro contains the command line flags smake is called with. The content is the same as the content of theMAKEFLAGSmacro except that no command line macros are added. TheMAKE_FLAGSmacro is exported into the environment.MAKE_ARCHβ set up by smake as part of the automake features of smake. It contains the processor architecture of the current machine fromuname -p(e.g.mc68020,sparc,pentium,i386).MAKE_BRANDβ set up by smake as part of the automake features of smake. It contains the brand of the current machine fromsysinfo(SI_HW_PROVIDER)(e.g.Sun_Microsystems).MAKE_DOMAINβ set up by smake as part of the automake features of smake. It contains the domainname of the current machine fromdomainname(1)(e.g.acme.com).MAKE_HOSTβ set up by smake as part of the automake features of smake. It contains the host name of the current machine fromuname -n(e.g.duffy,sherwood,ghost).MAKE_HOST_OSβ set up by smake as part of the automake features of smake. It contains the host OS name fromuname -Hsof the current machine in case that there is a layered OS like Cygwin (e.g.windows).MAKE_HOST_OSRELβ set up by smake as part of the automake features of smake. It contains the host OS release fromuname -Hrof the current machine in case that there is a layered OS like Cygwin (e.g.5.1).MAKE_HOST_OSVERSIONβ set up by smake as part of the automake features of smake. It contains the host OS version fromuname -Hvof the current machine in case that there is a layered OS like Cygwin (e.g.sp2).MAKE_HWSERIALβ set up by smake as part of the automake features of smake. It contains the serial number of the current machine (e.g.1920098175).MAKE_ISALISTβ set up by smake as part of the automake features of smake. It contains the instruction set architecture list of the current machine fromsysinfo(SI_ISALIST)(e.g.amd64 pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86).MAKE_M_ARCHβ set up by smake as part of the automake features of smake. It contains the machine architecture of the current machine derived fromMAKE_MACH(e.g.sun3,sun4).MAKE_MACHβ set up by smake as part of the automake features of smake. It contains the kernel architecture of the current machine fromuname -m(e.g.sun3,sun4c,sun4m,sun4u,i86pc).MAKE_MODELβ set up by smake as part of the automake features of smake. It contains the model name of the current machine fromsysinfo(SI_PLATFORM)oruname -i(e.g.SUNW,SPARCstation-20).MAKE_NAMEβ set up by smake as part of the automake features of smake. It contains the official name of the make program β in our casesmake.MAKE_OSβ set up by smake as part of the automake features of smake. It contains the operating system name of the current machine fromuname -s(e.g.sunos,linux,dgux).MAKE_OSDEFSβ set up by smake as part of the automake features of smake. It contains operating system specific defines for the compiler (e.g.-D__SVR4).MAKE_OSRELβ set up by smake as part of the automake features of smake. It contains the operating system release name of the current machine fromuname -r(e.g.5.5,4.1.1).MAKE_OSVERSIONβ set up by smake as part of the automake features of smake. It contains the operating system version of the current machine fromuname -v(e.g.generic).MAKE_SHELL_FLAGβ this macro contains the shell flags used when calling commands from smake. The default value is-ce. When smake is in POSIX mode, the default value is-c. TheMAKE_SHELL_FLAGmacro allows one to overwrite the default behavior.MAKE_SHELL_IFLAGβ this macro contains the shell flags used when calling commands fromsmake -i. The default value is-c. TheMAKE_SHELL_IFLAGmacro allows one to overwrite the default behavior.MAKE_VERSIONβ this macro contains the smake version number string.NUMBER_SIGNβ this macro is predefined to contain the number sign ('#') that is used as the make comment start character and for this reason cannot appear in non comment parts of regular makefiles.SHELLβ theSHELLmacro is neither imported nor exported from/to the environment. If it is set from within a makefile or from the commandline, the value is used as the name of an alternate shell to execute commands. Makefiles that setSHELL=should be used very carefully as not all platforms support all shells.VPATHβ this macro implements some object search functionality as found in other UNIX make programs. In smake this functionality is implemented using the.SEARCHLIST:special target, see chapter Search Rules For Files for more information.
π« DYNAMIC MACROS π«
Okay bestie, this is where it gets good. There's a whole lil squad of internal macros that update themselves dynamically, like they have main character energy and a personal stylist. Except for $O, you literally cannot overwrite them. Which is a flex, honestly.
Using them in explicit rules makes your makefile way more consistent β because the file names don't have to be typed in a second time (and we love not typing things twice π«Ά). Using them in implicit rules is, like, the only way to make implicit rules work at all, because the actual file name can't be coded into an implicit rule. Periodt.
If smake uses file name searching, the dynamic macros $<, $0, $n, $r1, $^, and $? are updated to reflect the actual path of the source or object directory. Slay.
The dynamic macros are:
$Oβ expands to the value of.OBJDIR. If.OBJDIRis not defined,$Oexpands to'.'. If$Ohas been overwritten, it may no longer be used as a reliable alias for.OBJDIR. (So like... don't do that, bestie π)$@β expands to the path name of the current target. Expanded for both explicit and implicit rules. π$*β expands to the path base name of the current target (that's the name of the current target with the suffix stripped off). Expanded for both explicit and implicit rules. POSIX requires that this macro is expanded at least for implicit rules.$<β expands to the path name of implicit source made in this step. Expanded for implicit rules only. The existence of this macro is required by POSIX.$0β expands to the path name of implicit source made in this step. Expanded for implicit rules only. This macro is available withsmakeonly. It's made available to make the behavior more orthogonal. (Iconic reasoning ngl β¨)$1$2$3... β expands to the path name of the *n*th file in the dependency list. Expanded for explicit rules only.$rnβ expands to the path names of all files in the dependency list starting with the *n*th. It is valid to specify$r0. Expanded for both explicit and implicit rules. The 0th entry is available only with implicit rules, the other entries are available only with explicit rules.$^β expands to the path names of all files in the dependency list. With implicit rules, it is identical to$r0; with explicit rules, it is identical to$r1.$?β expands to the path names of all files that are newer than the current target. Expanded for both explicit and implicit rules.
The following example shows how dynamic macros may be used together with file searching rules:
foo : main.o lex.o parse.o
$(CC) -o $@ $^
may expand to:
cc -o foo OBJ/main.o parser/OBJ/lex.o parser/OBJ/parse.o
Look at that glow-up π the file names just appear. Obsessed.
π§ IMPLICIT RULES π§
So if there's no explicit target rule for a specific target, smake tries to find a matching implicit rule. There are three different types of implicit rules, and the search order is very much a vibe check hierarchy:
- Pattern Matching Rules are searched first.
- If no matching pattern matching rule could be found, the Suffix Rules are checked.
- And if there was no matching suffix rule, the Simple Suffix Rules are checked.
Commands defined for implicit rules of any type typically make use of the dynamic macros $@, $* and $< as placeholders for target and dependency file names. π
β¨ Pattern Matching Rules β¨
Pattern matching rules were introduced by SunPRO make with SunOS-3.2 in 1986 (a literal elder icon). They are now also implemented in smake. Pattern matching rules are searched in the same order as they appear in the makefile β so no queue-jumping, bestie.
A pattern matching rule looks this way:
tp%ts: dp%ds
<tab> command
...
In this rule, tp is the target prefix, ts is the target suffix, dp is the dependency prefix and ds is the dependency suffix. Any of the parts may be a null string. The % part of the strings is the base name that matches zero or more characters in the target name. Whenever smake encounters a match with a target pattern of a pattern matching rule, it uses the matching base name to construct dependency names. If the target is out of date relative to the dependency, smake uses the commands from the pattern matching rule to build or rebuild the target.
A rule in the form:
tp%ts:
<tab> command
...
is permitted. (Yes, with no dependency at all. She's lowkey self-sufficient π¦)
πΈ Suffix Rules πΈ
Suffix rules are the POSIX way of specifying implicit dependencies. Suffix rules are searched when no pattern matching rule applies. If the special target .SUFFIXES: was cleared or is empty, smake does not check suffix rules. If .SUFFIXES: defines a list of suffixes, the current target file name is checked against this list. If there is a match, the list of suffix rules is checked against the target suffix and a dependency suffix that is also in the .SUFFIXES: list. The search order is derived from the order of suffixes in the suffix list while matching dependency suffixes. A suffix does not need to begin with a '.' to be recognized. (Chaotic, but valid. We respect it.)
A Double Suffix rule looks this way:
DsTs:
<tab> dependency_command
...
Ds is the dependency suffix and Ts is the target suffix. They are used when both target file name and dependency file name have a suffix. Double Suffix rules are searched before Single Suffix rules are checked.
A Single Suffix rule looks this way:
Ds:
<tab> dependency_command
...
Ds is the dependency suffix. Single Suffix rules are used when the target file name does not have a suffix.
π Simple Suffix Rules π
Simple suffix rules have the lowest preference when looking for implicit rules. And plot twist: simple suffix rules are specific to smake. It's giving exclusive membership π
A simple suffix rule looks this way:
target_suffix : dependency_1_suffix ...
<tab> dependency_1_command
...
The first line may not start with a TAB character. It starts with the target suffix or "" if no target suffix exists. The target suffix is followed by the target separator (':') and one or more dependency suffixes which are also written as "" if no dependency suffix exists. The first line is followed by exactly the same number of Bourne shell command lines (each starting with a TAB) as there were dependency suffix specifications on the right side of the first line. Each of the Bourne shell command lines corresponds to one of the dependency suffixes in the same order. No mixing and matching, bestie β the pairs are locked in π
When smake looks for a possible source for a target with a specific suffix, the dependency suffixes are tried out in the order they appear in the first line of the suffix rule. If a source is found, the corresponding command line is executed.
There may only one simple suffix rule per target suffix. All suffixes except the empty suffix ("") must start with a dot ('.').
The following example shows how a simple suffix rule for creating a zero suffix executable from a .o file may look:
"": .o
$(CC) -o $@ $<
A simple suffix rule that describes how to create a .o file from the possible .c and .s sources may look this way:
.o: .c .s
$(CC) -c $<
$(AS) -o $*.o $<
If smake is going to update foo.c using simple suffix rules, it will first look for a file foo.c and then for a file foo.s. If the file foo.c can be found the first command line ($(CC) -c $<) is executed, if the file foo.s can be found the second command line ($(AS) -o $*.o $<) is executed. Which command is executed depends on which source file exists. Detective work, honestly π
π₯ DEFAULT IMPLICIT RULES π₯
Changing the implicit rules allows one to change the default behavior of smake. That's the whole point, babe.
β Default Simple Suffix Rules β
The current default implicit rules, using the simple suffix rule notation, are compiled into smake:
FC= f77
RC= f77
PC= pc
AS= as
CC= cc
LEX= lex
YACCR= yacc -r
YACC= yacc
ROFF= nroff
RFLAGS= -ms
.o: .c .s .l
$(CC) -c $(CFLAGS) $0
$(AS) -o $*.o $0
$(LEX) $(LFLAGS) $0;$(CC) -c $(CFLAGS) lex.yy.c;rm lex.yy.c;mv lex.yy.o $@
.c: .y
$(YACC) $(YFLAGS) $0;mv y.tab.c $@
"": .o .sc
$(CC) -o $* $0
$(ROFF) $(RFLAGS) $0 > $@
If smake reads a file defaults.smk with default implicit rules, the compiled in rules are disabled. So like, she steps aside and lets the file take over. Respect. π
π ENVIRONMENT π
On startup, smake reads all environment variables (except the SHELL environment) into make macros. The following environment variables affect the behavior of smake. Bestie, take notes π
FORCE_SHELLβ If theFORCE_SHELLenvironment is present,smakebehaves as if the special target.FORCE_SHELL:appeared in a makefile.LANGβ Sets up a default value for internationalization variables that are unset or NULL.LC_ALLβ If set, this overrides any other internationalization variables. She's the boss πLC_CTYPEβ Determine the the interpretation of byte sequences.LC_MESSAGESβ Determine the diagnostic messages.MAKE_LEVELβ This variable is managed bysmaketo track nestedsmakecalls. It is initialized with1and exported. Nested calls tosmakeincrement the level. (Recursion but make it tracked π)MAKEFLAGSβ This variable is set up bysmakein order to forward command line options to nested calls tosmake. In order to do this,smakesets the content of theMAKEFLAGSvariable to contain a list of single character option letters. The option-fis not forwarded via theMAKEFLAGSenvironment. If command line macro definitions in the formname=valuehave been specified, the list of option letters is followed by the string" -- "and by a list of macro definitions.
If any of the name=value command line macro definitions contains a space (' ') or a backslash ('\'), these characters are escaped by a single backslash.
As required by POSIX, smake accepts all possible variants of formatting the content of the MAKEFLAGS environment.
See section Special Macros for more information on the MAKEFLAGS environment.
PATHβ ThePATHenvironment variable is used bysmakewhen calling commands. WhenPATHis modified, this may cause that specific commands cannot be found or that a different implementation with different behavior is found instead. β οΈ Like, be careful with yourPATH, bestie.PROJECTDIRβ This environment variable controls the way SCCS history files are searched for. Seesccs(1)for more information.
π ASYNCHRONOUS EVENTS π
okay so this part is lowkey dramatic β it's about what happens when you hit Ctrl+C mid-build π
If the shell hasn't already ignored them, smake catches SIGHUP, SIGINT, SIGQUIT and SIGTERM, and then removes the current target β but only unless one of these is true:
- the current target is a directory
- the current target is a prerequisite of the special target
.PRECIOUS - the special target
.PHONY(applies) - one of these options was given:
-t,-q,-por-n
β οΈ heads up bestie: that's a deletion, like the file is gone-gone. Protect your faves with.PRECIOUSor.PHONYbefore you start smashing Ctrl+C.
And there's this whole extra vibe check with bash(1): bash is known to handle signals in a non POSIX compliant way, because bash sets up different process groups for non-interactive commands π« So smake actively propagates the signals named above, in case the autoconfiguration figured out that /bin/sh is bash based. Teamwork, honestly.
β¨ EXIT STATUS β¨
Now, queen, the exit codes split into two eras depending on whether you passed -q π
When the -q option is specified, smake exits with one of these:
0β successful completion π1β the target was not up-to-date-1(255) β a command line usage error or makefile parsing error occurred>0β an error occurred
When the -q option is not specified, smake exits with one of these:
0β successful completion π-1(255) β a command line usage error or makefile parsing error occurred>0β an error occurred
yeah that's the whole bit β no 1-means-not-up-to-date energy in the non--q life π
π FILES π
Here's where smake goes looking for stuff, in order:
SMakefileMakefilemakefile
Smake first looks for SMakefile, then for Makefile, and then for makefile in the current directory, to find a list of rules to be used to resolve the targets π
defaults.smkβ ifsmakefinds this file in the current directory, then the implicit rules are read in from this file β¨/opt/schily/share/lib/smake/defaults.smkβ if the filedefaults.smkcould not be found, thensmaketries to read the implicit rules from this file. If this file cannot be found too, thensmakeuses the rules compiled intosmakeπ§
so like, three-tier fallback system, very main character of it honestly.
π¦ SEE ALSO π¦
dmake(1)make(1s)sh(1)sccs(1)sysctl(2)sysinfo(2)uname(2)makefiles(5)makerules(5)
πΈ NOTES πΈ
ngl this section is a whole saga, so buckle up π
If a platform uses a shell that does not handle signals correctly, and smake does not implement the work around that was mentioned above in the section ASYNCHRONOUS EVENTS, sub makes may continue to run even though the top level make was killed by a signal π Not great, not terrible, but definitely not great.
In order to be able to abort complex make structures after a failed command was encountered, the shell needs to behave correctly with the -e option. Bash version 3.x in general and the Korn Shell variant that comes with HP-UX are known to handle the -e option in a non-POSIX compliant way π If the autoconfiguration tests detect a non-compliant /bin/sh and there is a working Bourne Shell available under /bin/bosh or /opt/schily/bin/bosh, smake will use this bosh as the default shell. See the schily source consolidation at: https://sourceforge.net/projects/schilytools/files/
The old schily (SING) makefile system (until late 1999) did only define simple suffix rules. The current version of smake added support for pattern matching rules and POSIX suffix rules. These rules are considered to be rated with higher preference than simple suffix rules. If build in suffix rules can be found, current smake versions will not work correctly with old makefile systems π To use current smake versions with old makefile systems, call smake with the -r flag to disable build in POSIX suffix rules. Newer makefile system version include pattern matching rules that will be searched before the POSIX suffix rules π«Ά
β οΈ the-rflag thing is a real one: old makefile system + new smake = callsmake -r. periodt.
A new shell is run for each command line. If you want to run more complicated shell scripts, you need to escape the end of line with a backslash to keep all commands on one virtual shell command line π like, one vibe, one shell.
Smake tries to be as POSIX compliant as possible β¨
POSIX does not cover everything that is needed to write portable makefiles suitable to compile large portable projects. Even simple things like macro+=value are not covered by POSIX. Note that adding something to a macro definition is supported by all known make implementations since ~ 1980. In addition, it is most unlikely that different make implementations although POSIX compliant, are compatible enough to maintain large projects. For these reasons, it seems to be better to have a portable make implementation like smake π
π BUGS π
the receipts, in order:
- SCCS retrieval is not yet implemented π
- A command line that contains the macro
$(MAKE)is always executed, even when in POSIX mode. This is an intended conflict with the POSIX standard since POSIX.1-2008 π - Built in library handling is not yet implemented
- There are currently no other known bugs β¨ As
smakesince spring 1993 is used as the reference make program for the SCHILY (Sing) makefile system,smakeis the best choice when compiling projects that are using the SCHILY (Sing) makefile system π - Mail other bugs and suggestions to
schilytools@mlists.in-berlin.deor open a ticket athttps://codeberg.org/schilytools/schilytools/issuesπ«Ά - The mailing list archive may be found at:
https://mlists.in-berlin.de/mailman/listinfo/schilytools-mlists.in-berlin.de.
π§ AUTHOR π§
Joerg Schilling and the schilytools project authors π
β SOURCE DOWNLOAD β
The source code for smake is included in the schilytools project and may be retrieved from the schilytools project at Codeberg at
https://codeberg.org/schilytools/schilytools
The download directory is
https://codeberg.org/schilytools/schilytools/releases
and that's the whole page, bestie, you made it to the end ππ this was SMAKE(1) from the schilytools crew β author Joerg Schilling, dated 2022/10/06, and yes the manpage really is called SMAKE(1) with a (1) and everything π go build something iconic β¨π
π¦ where this page comes from
- opensuse-leap 16.0: smake 2022.10.16-160000.2.2 Β· CDDL-1.0
- opensuse-leap 16.0: smake 2022.10.16-160000.2.2 Β· CDDL-1.0
- opensuse-leap 16.0: smake 2022.10.16-160000.2.2 Β· CDDL-1.0
- opensuse-leap 16.0: smake 2022.10.16-160000.2.2 Β· CDDL-1.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms