As noted previously, one alternative to writing makefile s to the least common denominator is to adopt some standard tools. Of course, the goal is to make sure the standard tools are at least as portable as the application you are building. The obvious choice for portable tools are programs from the GNU project, but portable tools come from a wide variety of sources. Perl and Python are two other tools that come to mind.
In the absence of portable tools, encapsulating nonportable tools in make functions can sometimes do just as well. For instance, to support a variety of compilers for Enterprise JavaBeans (each of which has a slightly different invocation syntax), we can write a basic function to compile an EJB jar and parameterize it to allow one to plug in different compilers.
EJB_TMP_JAR = $(TMPDIR)/temp.jar # $(call compile-generic-bean, bean-type, jar-name, # bean-files-wildcard, manifest-name-opt ) define compile-generic-bean $(RM) $(dir $(META_INF)) $(MKDIR) $(META_INF) $(if $(filter %.xml %.xmi, ), \ cp $(filter %.xml %.xmi, ) $(META_INF)) $(call compile--bean-hook,) cd $(OUTPUT_DIR) && \ $(JAR) -cf0 $(EJB_TMP_JAR) \ $(call jar-file-arg,$(META_INF)) \ $(call bean-classes,) $(call -compile-command,) $(call create-manifest,$(if ,,),,) endef
The first argument to this general EJB compilation function is the type of bean compiler we are using, such as Weblogic, Websphere, etc. The remaining arguments are the jar name, the files forming the content of the jar (including configuration files), and an optional manifest file. The template function first creates a clean temporary area by deleting any old temporary directory and recreating it. Next, the function copies in the xml or xmi files present in the prerequisites into the $(META_INF) directory. At this point, we may need to perform custom operations to clean up the META-INF files or prepare the .class files. To support these operations, we include a hook function, compile-$1-bean-hook , that the user can define, if necessary. For instance, if the Websphere compiler required an extra control file, say an xsl file, we would write this hook:
# $(call compile-websphere-bean-hook, file-list) define compile-websphere-bean-hook cp $(filter %.xsl, ) $(META_INF) endef
By simply defining this function, we make sure the call in compile-generic-bean will be expanded appropriately. If we do not choose to write a hook function, the call in compile-generic-bean expands to nothing.
Next, our generic function creates the jar. The helper function jar-file-arg decomposes a normal file path into a -C option and a relative path:
# $(call jar-file-arg, file-name) define jar-file-arg -C "$(patsubst %/,%,$(dir ))" $(notdir ) endef
The helper function bean-classes extracts the appropriate class files from a source file list (the jar file only needs the interface and home classes):
# $(call bean-classes, bean-files-list) define bean-classes $(subst $(SOURCE_DIR)/,, \ $(filter %Interface.class %Home.class, \ $(subst .java,.class,))) endef
Then the generic function invokes the compiler of choice with $(call $1-compile-command,$2) :
define weblogic-compile-command cd $(TMPDIR) && \ $(JVM) weblogic.ejbc -compiler $(EJB_JAVAC) $(EJB_TMP_JAR) endef
Finally, our generic function adds the manifest.
Having defined compile-generic-bean , we wrap it in a compiler-specific function for each environment we want to support.
# $(call compile-weblogic-bean, jar-name, # bean-files-wildcard, manifest-name-opt ) define compile-weblogic-bean $(call compile-generic-bean,weblogic,,,) endef
7.4.1 A Standard Shell
It is worth reiterating here that one of the irksome incompatibilities one finds in moving from system to system is the capabilities of /bin/sh , the default shell used by make . If you find yourself tweaking the command scripts in your makefile , you should consider standardizing your shell. Of course, this is not reasonable for the typical open source project where the makefile is executed in uncontrolled environments. However, in a controlled setting, with a fixed set of specially configured machines, this is quite reasonable.
In addition to avoiding shell incompatibilities, many shells provide features that can avoid the use of numerous small utilities. For example, the bash shell includes enhanced shell variable expansion, such as %% and ## , that can help avoid the use of shell utilities, such as sed and expr .