From mboxrd@z Thu Jan 1 00:00:00 1970 Received: (from majordomo@localhost) by pauillac.inria.fr (8.7.6/8.7.3) id SAA04846; Fri, 21 Nov 2003 18:05:30 +0100 (MET) X-Authentication-Warning: pauillac.inria.fr: majordomo set sender to owner-caml-list@pauillac.inria.fr using -f Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by pauillac.inria.fr (8.7.6/8.7.3) with ESMTP id SAA04802 for ; Fri, 21 Nov 2003 18:05:29 +0100 (MET) Received: from yukon.yapper.org (adsl-67-120-175-89.dsl.lsan03.pacbell.net [67.120.175.89]) by concorde.inria.fr (8.11.1/8.11.1) with ESMTP id hALH5P112178 for ; Fri, 21 Nov 2003 18:05:26 +0100 (MET) Received: from cs.caltech.edu (kenai.yapper.org [67.120.175.90]) by yukon.yapper.org (Postfix) with ESMTP id 52E6E6A0; Fri, 21 Nov 2003 09:05:22 -0800 (PST) Message-ID: <3FBE45D2.5070309@cs.caltech.edu> Date: Fri, 21 Nov 2003 09:05:22 -0800 From: Jason Hickey Organization: Caltech Computer Science User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.4) Gecko/20030624 X-Accept-Language: en-us, en MIME-Version: 1.0 To: caml-list@inria.fr Cc: Martin Jambon Subject: Re: [Caml-list] Building large and portable projects References: In-Reply-To: Content-Type: multipart/mixed; boundary="------------010800030405060205050804" X-Loop: caml-list@inria.fr X-Spam: no; 0.00; hickey:01 caml-list:01 digests:01 subdirs:01 pserver:01 cvsroot:01 pserver:01 cvsroot:01 rpms:01 subdirs:01 foo:01 foo:01 libs:01 hickey:01 626:99 X-Attachments: name="omake.txt" name="omake.txt" Sender: owner-caml-list@pauillac.inria.fr Precedence: bulk This is a multi-part message in MIME format. --------------010800030405060205050804 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Martin Jambon wrote: > Is there a convenient way to develop OCaml code, and be sure that > this code will be configurable, compilable, installable and > executable without changes, on any environment where OCaml is available? We have been using omake to build several large projects, primarily on Linux and Windows. omake is written in OCaml, and provides a build system with syntax similar to make, but project-wide dependency analysis. Here are some features: - omake runs on Unix, Windows, MacOS, and presumably other architectures where OCaml is available. - dependency analysis is project-wide (like cons), based on MD5 digests - automated dependency analysis - there is builtin support for OCaml and C code, and it is easy to add support for other kinds of files (just like make). - the OMakefile syntax is similar to GNU make, but - omake has user-defined functions - OMakefile programs are functional - the .SUBDIRS target is used to define the project hierarchy - different parts of the project can have different configuration. omake is available by anonynous CVS from cvs.metaprl.org. % cvs -d :pserver:anoncvs@cvs.metaprl.org:/cvsroot login The password is anoncvs. % cvs -d :pserver:anoncvs@cvs.metaprl.org:/cvsroot checkout omake Alternatively, RPMs are available at rpm.nogin.org. Here is a short description. Every project must have an OMakeroot file in the project root. It is usually boilerplate; this is typical: # Include the standard configuration include $(STDROOT) # Include the OMakefile .SUBDIRS: . The project commands are then placed in an OMakefile. To build a standalone OCaml program from files a.ml b.ml and c.ml, you just need one line. The OCamlProgram function is defined in the system OMakeroot. OCamlProgram(foo, a b c) You can choose the byte-compiler, native-code compiler, or both. BYTE_ENABLED = true NATIVE_ENABLED = true OCamlProgram(foo, a b c) Maybe you have some C files you need to include in your compile as well. Perhaps f.c is a generated file. f.c: f1.c f2.c cat $+ > $@ StaticCLibrary(bar, d e f) LIBS = bar OCamlProgram(foo, a b c) Perhaps you use the C-preprocessor on some .mlp files: %.ml: %.mlp $(CPP) $*.mlp > $@ The system sources contain more examples, and the MetaPRL system, also available at cvs.metaprl.org, provides a very large, complex, example. Jason -- Jason Hickey http://www.cs.caltech.edu/~jyh Caltech Computer Science Tel: 626-395-6568 FAX: 626-792-4257 --------------010800030405060205050804 Content-Type: text/plain; name="omake.txt" Content-Transfer-Encoding: 8bit Content-Disposition: inline; filename="omake.txt" omake(1) MCC Programmer’s Manual omake(1) NAME omake - the omake build system SYNOPSIS omake is a replacement for the make(1) build program. The omake system provides the following additional features. Recursive builds Omake(1) is designed for building projects that might have source files in several directories. In Omake, projects are normally specified using an OMakefile in each of the project directories, and an OMakeroot file in the root directory. The OMakeroot file specifies general build rules, and the OMakefiles specify the build parameters specific to each of the subdirectories. When Omake runs, it walks the configuration tree, merging rules from all of the OMakefiles. The project is then built from the entire collection of build rules. Automatic dependency analysis. Dependency analysis has always been problematic with the make(1) pro- gram. Omake addresses this by adding the .SCANNER target, which speci- fies a command to produce dependencies. For example, the rule .SCANNER: %.c $(CC) $(INCLUDE) -MM $+ is the standard way to generate dependencies for .c files. Omake will automatically run the scanner when it needs to determine dependencies for a file. Content-based dependency analysis. Dependency analysis in omake uses MD5 digests to determine whether files have changed. After each run, omake stores the dependency infor- mation in a file called .omakedb in the project root directory. When a rule is considered for execution, the command is not executed if the target, dependencies, and command sequence are unchanged since the last run of omake. As an optimization, omake does not recompute the digest for a file that has an unchanged modification time, size, and inode number. OMakefile The OMakefile has a formal similar to Makefile. An OMakefile has three kinds of syntac objects: variable definitions, function definitions, and rule definitions. Variables Variables are defined with the following syntax. The name is any sequence of alphanumeric characters, underscore ’_’, and hyphen ’-’. = Values are represented as a sequence of literal characters and variable expansions. A variable expansion has the form "$()," which rep- resents the value of the "" variable in the current environment. Some examples are shown below. CC = gcc CFLAGS = -Wall -g COMMAND = $(CC) $(CFLAGS) -O2 In this example, the value of the COMMAND variable is the string "gcc -Wall -g -O2". Unlike make, variable expansion is eager, and functional. That is, variable values are expanded immediately and new variable definitions to not affect old ones. For example, consider the following variable definitions. X = $(COMMAND) COMMAND = $(COMMAND) -O3 Y = $(COMMAND) In this example, the value of the X variable is the string "gcc -Wall -g -O2" as before, and the value of the Y variable is "gcc -Wall -g -O2 -O3". Functions Functions are defined using the following syntax. () = The parameters are a comma-separated list of identifiers, and the body must be placed on a separate set of lines that are indented from the function definition itself. For example, the following text defines a function that concatenates its arguments. CONCAT(a, b) = $(a)$(b) Functions are called using the GNU-make syntax, "$( " is a comma-separated list of values. Built-in functions addsuffix The addsuffix function adds a suffix to each component of a white-space separated list of strings. Double-quotes may be used to delimit components that contain spaces. $(addsuffix , , , , ) For example, $(set z y z "m n" w a) expands to "m n" a w y z. not Boolean values in omake are represented by case-insensitive strings. The false value can be represented by the strings false, no, nil, undefined or 0, and everything else is true. The not func- tion negates a Boolean value. $(not ) For example, $(not false) expands to the string true, and $(not hello world) expands to false. equal The equal function tests for equality of two values. $(equal , ) For example $(equal a, b) expands to false, and $(equal hello world, hello world) expands to true. if The if function represents a conditional based on a Boolean value. $(if , ) For example $(if $(equal a, b), c, d) expands to d. filter-out The filter-out function removes a component from a white- space separated list of strings. $(filter-out , )$ For example $(filter-out a, c d a "hello world") expands to c d "hello world". file The file and dir functions define location-independent references to files and directories. In omake, the commands to build a target are executed in the target’s directory. Since there may be many directories in an omake project, the build system provdes a way to construct a reference to a file in one directory, and use it in another without explicitly modifying the file name. The functions have the following syntax, where the name should refer to a file or directory. $(file ) , $(dir ) For example, we can construct a reference to a file "foo" in the cur- rent directory. FOO = $(file foo) .SUBDIRS: bar If the FOO variable is expanded in the bar subdirectory, it will expand to ../foo. These commands are often used in the top-level OMakefile to provide location-independent references to top-level directories, so that build commands may refer to these directories as if they were absolute. ROOT = $(dir .) LIB = $(dir lib) BIN = $(dir bin) Once these variables are defined, they can be used in build commands in subdirectories as follows, where $(BIN) will expand to the location of the bin directory relative to the command being executed. install: hello cp hello $(BIN) in The in function is closely related to the dir and file functions. It takes a directory and a file, and gives the filename expanded relative to that directory. For example, one common way to install a file is to define a symbol link, where the value of the link is relative to the directory where the link is created. $(in , ) The following commands create links in the $(LIB) directory. FOO = $(file foo) install: ln -s $(in $(LIB), $(FOO)) $(LIB)/foo Rules Rules specify commands to solve a file dependency. At its simplest, a rule has the following form. : For example, the following rule specifies how to compile a file hello.c. hello.o: hello.c $(CC) $(CFLAGS) -c -o hello.o hello.c This rule states that the hello.o file depends on the hello.c file. If the hello.c file is newer, the command "$(CC) $(CFLAGS) -c -o hello.o hello.c" is to be executed to update the target file hello.o. A rule can have an arbitrary number of commands. The individual com- mand lines are executed independently by the shell sh(1). The commands do not have to begin with a tab, but they must be indented from the dependency line. Rules can also be implicit. That is, the files may be specified by wildcard patterns. The wildcard character is %. For example, the fol- lowing rule specifies a generic way to build .o files. %.o: %.c $(CC) $(CFLAGS) -c -o $@ $*.c This rule is a general rule to build an arbitrry .o file from a .c file. The variables $@ and $* are special. The following variables may be used in rules. $*: the target name, without a suffix. $@: the target name. $^: a list of the sources, with duplicates removed. $+: a list of the sources, dpulicates are not removed. $<: the first source Unlike normal values, the variables in a rule body are expanded lazily, and binding is dynamic. The following function definition illustrates some of the issues. CLibrary(name, files) = OFILES = $(addsuffix .o, $(files)) $(name).a: $(OFILES) $(AR) cq $@ $,(OFILES) This function defines a rule to build a program called name from a set of .o files. The files in the argument are specified without a suffix, so the first line of the function definition defines a variable OFILES that adds the .o suffix to each of the file names. The next step defines a rule to build a target library "$(name).a" from the "$(OFILES)" files. The variable expansion "$,(OFILES)" specifies that eager expansion should be used on the OFILES variable. Special targets .PHONY The ".PHONY" target specifies that a target name does not actu- ally correspond to a file. Phony targets are often used to specify extra dependencies. For example, the "all" target is frequently used to specify a set of files that should be constructed by default, but the "all" target does not correspond to a file. .PHONY: all all: hello.o .DEFAULT Omake does not build the target specified by the first rule in an OMakefile (since there are several OMakefiles). The .DEFAULT phony target specifies what targets should be built by default. .DEFAULT: hello.o .SCANNER The ".SCANNER" target specifies a command to produce depen- dency information for a file. The command should produce output that is compatible with omake. For example, the following rule uses gcc(1) to generate dependencies for .c files. .SCANNER: %.c $(CC) $(CFLAGS) -MM $^ .SUBDIRS The ".SUBDIRS" target is used to specify subdirectories con- taining omake projects. Each of the subdirectories should have an OMakefile. For example, the following line specifies a project with three sub-projects: foo, bar, and baz. .SUBDIRS: foo bar baz The current environment is inherited by the subdirectories. This can be useful if different options are ot be used in different sub- directories. For example, the following lines specify that foo should be built in debug mode, while bar and baz should be opti- mized. CFLAGS = -g .SUBDIRS: foo CFLAGS = -O2 .SUBDIRS: bar baz NOTES Variables in omake are functional, and new variable definitions shadow old ones. In some cases it is convenient to specify a separate set of variables for different build targets. A frequent idiom in this case is to use a trivial conditional to define a separate scope. if 1 CFLAGS = -g .SUBDIRS: foo .SUBDIRS: bar baz The "CFLAGS = -g" variable is defined in a scope used only by the foo subdirectory, not by the bar and baz directories. It is generally suspected that omake contains a special prize ^_^ FLAGS -k: do no stop when an error is encountered. -j : specify the maximum number of parallel jobs to run. -n: do not execute the commands to build the target, but print what would be executed instead. -s: be silent and do not print commands as they are executed. -t: "touch" the database to make it seem that all files are up to date. -U: flush the dependency cache and rebuild all files from scratch. -u: a weaker version of the above that keeps the dependency cache but requires that the OMakefile configuration tree be re-read. FILES OMakefile, OMakeroot SEE ALSO make(1) LibMojave 2003-04-12 omake(1) --------------010800030405060205050804-- ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners