Zmake Generation

Zmake Generation




1 Goal of a make process


A maker has three tasks:

The last of the three points is one of the basic idea of make, but it is not used often. Make files are written for a specified target and only the really necessary translations are contained there.

The second of the three points are a problem often: The basic idea for make created with C and UNIX in the 1970-th was: Changed files have a new timestamp. If a file was copied from an older archive, the copy of the old file gets the current time. So the maker detects that a translation is necessary in comparison to the time stamp of the now older destination file.

The property that copied files get a new timestamp is a Unix/Linux property. In Windows copied files get the timestamp from its source, the old one. It is a philosphy what is better. Some people (inclusive me) means, keep the old timestamp while copying is the better one. The timestamp is an interesting property of a file. If I see a file with timestamp 2003-... or 1997-... I know it is an older one. Often I know the context of history of the file viewing its timestamp. Files can be sorted in a file viewer by timestamps. If copied older files lost its time stamp information I don't see what's happen. I should look into the files, is there a version information? Searching with eyes, not sorting automatically, effort. Of course a file version management system can show all that information, but ...

Most of maker tools does not work properly if older files are copied with its older timestamp. They don't detect the necessity of translation. Therefore 'make all' is the decision often in practice. But 'make all' runs a long time. The second goal of make is absent. A maker is degraded only to a simple organisator of execution.

There is a second problem of the second of the three points: Detection of necessity of translation because a changed indirect depending source: Usual the dependency of the destination from sources are written in a make file in form:

destination: source1 source2 source3

The make script file should name the dependency explicitely. But in praxis C-sources includes headers, the headers includes other headers, there are dependent. But nobody corrects the dependency line in the makefile, because it is sophisticated often.

If a make process 'forgets' the compilation of a C-file because the not known dependency to a changed header, an obscure behavior can be occured which is difficult to detect and debug (data addresses are shifted, wrong data, addresses of virtual methods are shifted, it calls the wrong methods).

Because the non-detected dependencies the decision is again: 'make all'.

A third problem is given if the sources of the make process are generated (second sources). That is because any superior development tool is used mostly for graphic programming. The generated source have a new timestamp though they do not may have a change content. In actual fact the translation is not necessary, but the fact cannot be detected by a maker tool. Therefore all is maked with high execution time effort.

It means, the second goal of a maker unprofitable. A standard maker is degraded to a tool which is a simple execution control script only with a special sophisticated syntax.

2 Goal of make in Zmake


Zmake in its new form is only a simple execution script written in JZcmd syntax.

But the second goal of make: checking the necessity of translation by timestamp check with dependency check can be done inside the translation execution routines with special convenient sub tools. The simplest one is: Comparison of time stamp of 2 files for conditionally execution:

if(FileSystem.checkNewless(fileSrc, fileDst, 1) > 0 ){

With 1 as third parameter the fileDst will be removed to force translation by checking the existence of the file in a later executed step:

if( not fileDst.exists()) {

Both problems, the complex dependency check and the detection of changed or non-changed generated (second) sources is resolved by the tool CheckDeps_C which can invoked inside the JZcmd zmake script.

3 Zmake file as JZcmd file, ZmakeUserScript


The idea of separation of ZmakeGenCtrlScript from the ZmakeUserScript may be a good but not necessary decision. It is possible to have only one script. The ZmakeGenCtrlScript is included.

ZmakeUserScript: ===================JZcmd============+=======>ZmakeExecutionScript:
Keep the users make                   ^              |        Generate the ready to use
control file simple.                  |              |        make file to execute
                             ZmakeGenCtrlScript:     |
                             Use a second file,      +=======>Execution direct
                             which controls details           (Java methods or cmd line)
                             of the make process.

4 Pattern for a ZmakeUserScript


For an example see examples_JZcmd:zmake-compile/exampleCcompile.jz.bat

The script starts with call of JZcmd. In this case it is assumed that a Windows-batch-file or unix shell script jzcmd can be found in the PATH. The jzcmd script gets the name of the file itself as first parameter.

jzcmd examplemakefile.jz.bat -t:textout.txt

Alternatively a jzcmd.bat can be located with relative path in the environment. For content of jzcmd see Topic:.JZcmd.invoke.cmd.:

call ..\..\myTools\jzcmd.bat examplemakefile.jz.bat -t:textout.txt

In this case the tool base for JZcmd is found in the local context.

exit /B

That are Windows cmd statements to prevent closing the command line window.


After that marker the JZcmd script starts.

include $ZBNFJAX_HOME/zmake/genXMI_exec.jzcmd;
include ../../myTools/myZmake.jzcmd;

It is possible to include scripts, which are ready to use for the Zmake process. Some scripts are given in the zbnfjax environment. Some scripts may be user specific. The includes scripts will be parsed and translated in the time of execution of this script.

currdir = scriptdir;

Usual the current directory used for absolute file paths is set to the directory where this scriptfile is stored.

currdir = <:><&scriptdir>/../..<.>;

It is possible and recommended that the current directory is set to a parent location especially if the make script is stored in a sub directory of the local working area in the file system. It is recommended to use relative paths.

Fileset mySources =
( srcpath/file1.ext
, srcpath2/*.c

A Fileset determines some files which are used for input. There may be more as one Fileset for some operations.

String paramXyz= "-flags -options";

Some parameter for some stuff.

Filepath objfiles = "test/obj/*.obj";

It is a path and the extension for obj-files for compilation, for example.

The script can define all zmake targets in its main routine. Of course it can be written in a sub routine too, which is called from main().

  call openDependencyCheck(
    cfgfile = Filepath "cfgCheckDep.cfg"
  , depfile = Filepath "dependencies.txt");

  call makeExample();

  call closeDependencyCheck();
  <+><.+close>;                    ##closes the text output
  cmd cmd /C _gen.bat;

In the main the openDependencyCheck-sub routine is called. A configuration file with include paths and the dependency-file is given as arguments. The dependency-file is created firstly or it is used for check of timestamps and dependencies see CheckDeps_C

The main routine can containt the zmake statements immediately. It is a better style to call a subroutine.

The closeDependencyCheck() writes the current situation of timestamps of all files and dependencies after processing all checkDeps.processSrcfile(...) in the zmake generation script.

At last in this pattern the generated batch-file is executed in the JZcmd script. Alternatively it can be executed in the batch part of this file or it can be invoked in any other environment.

The zmake lines are written in a sub routine:

##One of the zmake user routines.
sub makeExample(){
  zmake dstpath/file.ext := makeroutine(&mySources, paramx=paramXyz);

zmake is the invocation of a zmake target. The makeroutine should be found in the included scripts. There can be more as one zmake call, some other JZcmd statements, sub routines etc.

Syntax for zmake see Topic:.JZcmd.syntax.zmake.

See examples ../examples_JZcmd/Java2Xmi/readme.html, ../examples_JZcmd/Cheader2Xmi/readme.html, examples_JZcmd/zmake-compile/readme.html

That is all for the zmake user script. It may be more complex if the task is more complex.

5 Zmake file as JZcmd file, ZmakeGenScript


The ZmakeUserScript can contain the initializing of Checkdeps, in the pattern above the call openDependencyCheck(...) This subroutine and the close routine is programmed here. A variable checkDeps is defined on script level in the include script.

Obj checkDeps = new org.vishia.checkDeps_C.CheckDependencyFile(console, 1);

sub openDependencyCheck(Filepath cfgfile = "cfgCheckDep.cfg", Filepath depfile = "dependencies.txt"){
  checkDeps.readCfgData(cfgfile.absfile(), currdir);

sub closeDependencyCheck(){

The included JZcmd script should contain the requested zmake routines.

sub makeroutine(Obj target, String paramx = "default"){

The target is an instance of javadoc-vishiaBase/org/vishia/cmd/ZmakeTarget which contains the input FileSet (maybe more as one) and the output Filepath which is given in the zmake ...-line. The paramx is an example for a second parameter of this routine with a default value which can be given in the zmake line.

Usual some file should be handled. The next line gathers all given files of the target's input in a List<File>;

  List inputsExpanded = target.allInputFilesExpanded();

Sometime anything should be done with each input file per file. Therefore a for statement of JZcmd can be used. First the showing of the filename in the stdout is programmed.

  for(input:inputsExpanded) {
    <+out><&input.localfile()><.+n>   ##show in cmd line window
    //some statements for the file

If the input file should produced an output file only if it is changed the checker for dependency is used. It works for C/C++-sources. That routine deletes the object file. Therefore it can be ask whether a compilation is necessary by testing the existence of the object file:

    checkDeps.processSrcfile(input.absfile(), input.localfile(), objDirExt.absdir(), ".obj");

The routine nextNr is a special feature of JZcmd and produces an unique label for a windows batch file. The objname builds a proper String which is used more as one in the next statements:

    String nextNr1 = nextNr;   ##provides an incremented number
    String objname = <:><&objDirExt.absdirW()>\<&><.>;

The next statements are written in the JZcmd output text file, not executed. They are executed after the text output is generated in a system's batch invocation. It is an example for MS-Windows. Adequate shell scripts for UNIX may be written.

      REM <&objDirExt.absdir()>
      if exist /out:<&target.output.absdirW()>\obj\<&>.obj goto :<&nextNr1>
      cl.exe /c /TP <&incPath> <&input.absfileW()> /Fo<&objname><&objDirExt.ext()> 1><&objname>.log 2><&objname>.err

In the batch file the existence of the object file is tested. The compiler, in this example from MS Visual studio, is invoked conditionally.

The separation of execution with the JZcmd script and with a generated script file is a possibility, not a necessity. It have some maybe advantages and features:

A compiler can be invoked in the JZcmd script immediately. The invocation of compile creates an own Process on the system. One should write for example:

   String msg = cmd cl.exe /c /TP &incPath &input.absfileW() <:>/Fo<&objname><.> objDirExt.ext();
   if(msg >= "error"){
       Compilation error for <&input.localfile()>

That form has the advantage, that an evaluation of stdout or any other messages can be done with the capablity of JZcmd. That's some more as in batch files.