Existing Target Modes
This page explains the possible modes to handle existing target files. If the generator encounters a file to be generated which already exists, it can be instructed to treat it the following ways:
-
replace
(default): The existing file is replaced by the newly generated file. -
skip
: Generation is skipped, the existing file is left as is. -
append
: The generation result is appended to the content of the existing file. -
merge
: The generation result merged with the content of the existing file.
The skip mode is suitable when a starting point for a file should be generated, which can then subsequently be edited by the user.
The append mode can be useful for generating log files and similar.
The merge mode is suitable when a file should be generated which content can then either be changed by the user and the generator.
Allow user to change generated code
Three of the modes above allows the user to change generated files: skip, append and merge. From those, append is unlikely to appear in code generation so it will not be considered further. The two other modes, skip and merge, have different merits in user-generator interaction.
The mode skip
should be used if the generated file is just a starting
point, or if the generation result is very unlikely to change,
e.g. if the code templates are believed to have all needed features
and the sources are unlikely to change.
It is a strategy which is simple to understand and use, so if a problem
can be solved by both the skip
mode and the merge
mode,
the skip mode should be used.
The mode merge
should be used if the generated code is not stable yet
(e.g. because the generator lacks features, or the source is likely
to change in such a way that the contents of a generated file is changed),
and it is likely that user interaction is needed in the generated files.
Even if the above is the case, it may be better to separate generated
content and user-written content by using a delegation or inheritance
strategy (e.g. see the inheritance strategy in the Torque templates).
Delegation or inheritance will usually work if the user changes
involve changing whole methods; it will not work if changes involve only
parts of methods and these parts cannot (or should not) be factored out.
The reasons for trying to avoud merging are:
- Merging is complicated to understand.
- Merging is complicated to use.
Merge Mode
The merge
mode works as follows:
If a file is generated which existingTargetStrategy is
merge
is generated, the generator will store the generated
code in two files: one file being the nominal target, and the other
file is stored in another directory tree
(e.g. src/unmerged-gen-output/).
Once this has happened, the user can change the generated file
(all changes should be in the nominal target file, the file in
src/unmerged-gen-output/ should _NOT_ be edited),
Also changes can be made to the generator.
As an example, let us assume that in the second generator run,
both the file which was generated in the first generator run
and the generation result have changed.
The generator will now produce the generation output in memory
and will compare
a) the src/unmerged-gen-output file with the new generation result and
b) the src/unmerged-gen-output file with the nominal target file.
This way the generator can determine which changes were made
in the generator output and which changes were made by the user.
Then, the generator will perform a three-way-merge with the two
changesets and overwrite the nominal target file with the results
of the merge and the src/unmerged-gen-output file with the raw
(unmerged) generation result.
In the case that conflicts appear in the merge,
the merger will produce the usual < and > marked sections
with both the conflicting inputs.
Of course, other generation runs can follow, with either the user
editing the generated file, wit changed generation output, or both.
There are a couple of things to consider when operating in merge mode:
- Conflicts should be resolved before attempting a new generation run. Otherwise, the < and > marked sections will probably produce more conflicts, eventually ending in < and > merge hell.
- When several copies of the project exist where the generation is run, the unmerged files (in src/unmerged-gen-output in the above example) should also be shared between the copies (this usually implies that they are checked in version control). Otherwise, the three-way-merge can produce strage results because the merge basis is different in different copies.
- If the unmerged files (in src/unmerged-gen-output in the above example) are checked into version control, care should be taken to always check in the nominal target files and the src/unmerged-gen-output files in the same commit; otherwise different people may use a different merge basis which can produce a different merge result.