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 replace mode is suitable when the target file is not intended to be edited.
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.
That said, there are situations where merging saves a lot of pain, so it should be considered whenever generated and user-written code are very interwoven.

Merge Mode

The merge mode works as follows: If a file is generated which existingTargetStrategy is mergeis 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.