ocaml/tools/Makefile

398 lines
12 KiB
Makefile
Raw Normal View History

#**************************************************************************
#* *
#* OCaml *
#* *
#* Xavier Leroy, projet Cristal, INRIA Rocquencourt *
#* *
#* Copyright 1999 Institut National de Recherche en Informatique et *
#* en Automatique. *
#* *
#* All rights reserved. This file is distributed under the terms of *
#* the GNU Lesser General Public License version 2.1, with the *
#* special exception on linking described in the file LICENSE. *
#* *
#**************************************************************************
MAKEFLAGS := -r -R
ROOTDIR = ..
2018-07-26 08:27:00 -07:00
include $(ROOTDIR)/Makefile.common
ifeq ($(SYSTEM),unix)
override define shellquote
$i := $$(subst ",\",$$(subst $$$$,\$$$$,$$(subst `,\`,$i)))#")#
endef
$(foreach i,BINDIR LIBDIR STUBLIBDIR MANDIR,$(eval $(shellquote)))
endif
DESTDIR ?=
# Setup GNU make variables storing per-target source and target,
# a list of installed tools, and a function to quote a filename for
# the shell.
2020-05-11 08:50:29 -07:00
override installed_tools := ocamldep ocamlprof ocamlcp ocamloptp \
ocamlmktop ocamlmklib ocamlobjinfo
install_files :=
define byte2native
$(patsubst %.cmo,%.cmx,$(patsubst %.cma,%.cmxa,$1))
endef
# $1 = target, $2 = OCaml object dependencies, $3 = other dependencies
# There is a lot of subtle code here. The multiple layers of expansion
# are due to `make`'s eval() function, which evaluates the string
# passed to it as a makefile fragment. So it is crucial that variables
# not get expanded too many times.
define byte_and_opt_
# This check is defensive programming
$(and $(filter-out 1,$(words $1)),$(error \
cannot build file with whitespace in name))
$(call PROGRAM_SYNONYM, $1)
$1$(EXE): $3 $2
2018-07-26 08:27:00 -07:00
$$(CAMLC) $$(LINKFLAGS) -I $$(ROOTDIR) -o $$@ $2
$(call PROGRAM_SYNONYM, $1.opt)
$1.opt$(EXE): $3 $$(call byte2native,$2)
2018-09-06 07:12:42 -07:00
$$(CAMLOPT_CMD) $$(LINKFLAGS) -I $$(ROOTDIR) -o $$@ \
$$(call byte2native,$2)
all: $1
opt.opt: $1.opt
ifeq '$(filter $(installed_tools),$1)' '$1'
install_files += $1
endif
clean::
rm -f -- $1 $1.opt $1.exe $1.opt.exe
endef
# Escape any $ characters in the arguments and eval the result.
define byte_and_opt
$(eval $(call \
byte_and_opt_,$(subst $$,$$$$,$1),$(subst $$,$$$$,$2),$(subst $$,$$$$,$3)))
endef
CAMLC = $(BOOT_OCAMLC) -g -nostdlib -I $(ROOTDIR)/boot \
-use-prims $(ROOTDIR)/runtime/primitives -I $(ROOTDIR)
CAMLOPT = $(CAMLRUN) $(ROOTDIR)/ocamlopt$(EXE) -g -nostdlib -I $(ROOTDIR)/stdlib
CAMLLEX = $(CAMLRUN) $(ROOTDIR)/boot/ocamllex
INCLUDES = $(addprefix -I $(ROOTDIR)/,utils parsing typing bytecomp \
middle_end middle_end/closure middle_end/flambda \
middle_end/flambda/base_types driver toplevel \
file_formats lambda)
COMPFLAGS = -absname -w +a-4-9-41-42-44-45-48 -strict-sequence -warn-error A \
-principal -safe-string -strict-formats -bin-annot $(INCLUDES)
LINKFLAGS = $(INCLUDES)
VPATH := $(filter-out -I,$(INCLUDES))
.PHONY: all allopt opt.opt # allopt and opt.opt are synonyms
allopt: opt.opt
# The dependency generator
CAMLDEP_OBJ=ocamldep.cmo
CAMLDEP_IMPORTS= \
2018-07-26 08:27:00 -07:00
$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma
ocamldep$(EXE): LINKFLAGS += -compat-32
$(call byte_and_opt,ocamldep,$(CAMLDEP_IMPORTS) $(CAMLDEP_OBJ),)
ocamldep$(EXE): depend.cmi
ocamldep.opt$(EXE): depend.cmi
clean::
rm -f ocamldep ocamldep.exe ocamldep.opt ocamldep.opt.exe
# The profiler
CSLPROF=ocamlprof.cmo
CSLPROF_IMPORTS=config.cmo build_path_prefix_map.cmo misc.cmo identifiable.cmo \
numbers.cmo arg_helper.cmo clflags.cmo terminfo.cmo \
warnings.cmo location.cmo longident.cmo docstrings.cmo \
syntaxerr.cmo ast_helper.cmo \
camlinternalMenhirLib.cmo parser.cmo \
pprintast.cmo \
lexer.cmo parse.cmo
$(call byte_and_opt,ocamlprof,$(CSLPROF_IMPORTS) profiling.cmo $(CSLPROF),)
ocamlcp_cmos = config.cmo build_path_prefix_map.cmo misc.cmo profile.cmo \
warnings.cmo identifiable.cmo numbers.cmo arg_helper.cmo \
clflags.cmo \
terminfo.cmo location.cmo load_path.cmo ccomp.cmo compenv.cmo \
main_args.cmo
$(call byte_and_opt,ocamlcp,$(ocamlcp_cmos) ocamlcp.cmo,)
2020-05-11 08:50:29 -07:00
$(call byte_and_opt,ocamloptp,$(ocamlcp_cmos) ocamloptp.cmo,)
opt:: profiling.cmx
install::
$(INSTALL_DATA) \
profiling.cmi profiling.cmo \
makefiles: use 'install' instead of 'cp' in 'make install' targets I can observe weird performance bottlenecks on my machine caused by the use of 'cp' in the 'install' scripts of OCaml. When installing into a directory that is already populated by an existing installation, 'make install' can routinely take 10s on my machine¹. After this change it reliably takes 1.5s, independently of whether the destination is already populated or not. ¹: a brtfs filesystem on an old-ish SSD Why I care ---------- An extra 10s delay due to 'make install' can be noticeable in tight change-build-install-test feedback loops for a compiler change where we change the compiler, have a fast 'make world.opt' due to incremental builds, install the change and test it -- possibly after installing a couple opam packages, which can be fairly quick. Partial diagnosis ----------------- The performance issue seems to be caused by the fact that 'cp' (at least the GNU coreutils version), when the file already exists, replaces it by opening it in writeonly+truncate mode and writing the file content ('strace' shows that the delay is caused within an 'openat' call). In particular, using the --remove-destination option (which changes 'cp' to just remove the destination file before copying) removes the performance issue, but this option seems missing from the BSD/OSX 'cp' so it could cause portability issue. Change ------ The present commit rewrites the 'install' targets of all Makefiles to use the 'install' command instead. 'install' by default gives executable-like permission to the destination file, instead of reusing the source file's permissions, so we specify manually the permission modes, depending on whether the installed file is an executable (or dynamically-linked library) or just data (including other compiled object files). Testing ------- I checked manually that the permissions of the installed files are identical to the ones of the current 'cp'-using targets, except for some '.mli' file in middle_end which currently have +x bits enabled for no good reason. Remark: To test this, playing with the DESTDIR variable is very useful (this lets you install to a new directory (or the same as before) without having to re-run the configure script). I used the following, fairly slow shell script to collect permissions: for f in $(find $DESTDIR); do \ echo $(basename $f) $(ls -l $f | cut -d' ' -f1); \ done | sort Remark: it is important to run `sync` in-between 'make install' runs to avoid timing effects due to filesystem or disk caching strategies. I believe that this corresponds to the natural time delay (and unrelated disk activity) that would occur in realistic change-install-test feedback loops.
2018-03-28 08:46:34 -07:00
"$(INSTALL_LIBDIR)"
ifeq "$(INSTALL_SOURCE_ARTIFACTS)" "true"
$(INSTALL_DATA) \
profiling.cmt profiling.cmti \
"$(INSTALL_LIBDIR)"
endif
installopt::
$(INSTALL_DATA) \
makefiles: use 'install' instead of 'cp' in 'make install' targets I can observe weird performance bottlenecks on my machine caused by the use of 'cp' in the 'install' scripts of OCaml. When installing into a directory that is already populated by an existing installation, 'make install' can routinely take 10s on my machine¹. After this change it reliably takes 1.5s, independently of whether the destination is already populated or not. ¹: a brtfs filesystem on an old-ish SSD Why I care ---------- An extra 10s delay due to 'make install' can be noticeable in tight change-build-install-test feedback loops for a compiler change where we change the compiler, have a fast 'make world.opt' due to incremental builds, install the change and test it -- possibly after installing a couple opam packages, which can be fairly quick. Partial diagnosis ----------------- The performance issue seems to be caused by the fact that 'cp' (at least the GNU coreutils version), when the file already exists, replaces it by opening it in writeonly+truncate mode and writing the file content ('strace' shows that the delay is caused within an 'openat' call). In particular, using the --remove-destination option (which changes 'cp' to just remove the destination file before copying) removes the performance issue, but this option seems missing from the BSD/OSX 'cp' so it could cause portability issue. Change ------ The present commit rewrites the 'install' targets of all Makefiles to use the 'install' command instead. 'install' by default gives executable-like permission to the destination file, instead of reusing the source file's permissions, so we specify manually the permission modes, depending on whether the installed file is an executable (or dynamically-linked library) or just data (including other compiled object files). Testing ------- I checked manually that the permissions of the installed files are identical to the ones of the current 'cp'-using targets, except for some '.mli' file in middle_end which currently have +x bits enabled for no good reason. Remark: To test this, playing with the DESTDIR variable is very useful (this lets you install to a new directory (or the same as before) without having to re-run the configure script). I used the following, fairly slow shell script to collect permissions: for f in $(find $DESTDIR); do \ echo $(basename $f) $(ls -l $f | cut -d' ' -f1); \ done | sort Remark: it is important to run `sync` in-between 'make install' runs to avoid timing effects due to filesystem or disk caching strategies. I believe that this corresponds to the natural time delay (and unrelated disk activity) that would occur in realistic change-install-test feedback loops.
2018-03-28 08:46:34 -07:00
profiling.cmx profiling.$(O) \
"$(INSTALL_LIBDIR)"
# To help building mixed-mode libraries (OCaml + C)
$(call byte_and_opt,ocamlmklib,ocamlmklibconfig.cmo config.cmo \
build_path_prefix_map.cmo misc.cmo ocamlmklib.cmo,)
ocamlmklibconfig.ml: $(ROOTDIR)/Makefile.config Makefile
(echo 'let bindir = "$(BINDIR)"'; \
echo 'let supports_shared_libraries = $(SUPPORTS_SHARED_LIBRARIES)';\
echo 'let default_rpath = "$(RPATH)"'; \
echo 'let mksharedlibrpath = "$(MKSHAREDLIBRPATH)"'; \
echo 'let toolpref = "$(TOOLPREF)"';) \
> ocamlmklibconfig.ml
beforedepend:: ocamlmklibconfig.ml
clean::
rm -f ocamlmklibconfig.ml
# To make custom toplevels
OCAMLMKTOP=ocamlmktop.cmo
OCAMLMKTOP_IMPORTS=config.cmo build_path_prefix_map.cmo misc.cmo \
identifiable.cmo numbers.cmo arg_helper.cmo clflags.cmo \
load_path.cmo profile.cmo ccomp.cmo
$(call byte_and_opt,ocamlmktop,$(OCAMLMKTOP_IMPORTS) $(OCAMLMKTOP),)
# Converter olabl/ocaml 2.99 to ocaml 3
LIBRARY3=config.cmo build_path_prefix_map.cmo misc.cmo warnings.cmo location.cmo
ifeq ($(UNIX_OR_WIN32),unix)
LN := ln -sf
else
LN := cp -pf
endif
install::
ifeq "$(INSTALL_BYTECODE_PROGRAMS)" "true"
for i in $(install_files); \
do \
$(INSTALL_PROG) "$$i$(EXE)" "$(INSTALL_BINDIR)/$$i.byte$(EXE)"; \
if test -f "$$i".opt$(EXE); then \
$(INSTALL_PROG) "$$i.opt$(EXE)" \
"$(INSTALL_BINDIR)/$$i.opt$(EXE)" && \
(cd "$(INSTALL_BINDIR)/" && $(LN) "$$i.opt$(EXE)" "$$i$(EXE)"); \
else \
(cd "$(INSTALL_BINDIR)/" && $(LN) "$$i.byte$(EXE)" "$$i$(EXE)"); \
fi; \
done
else
for i in $(install_files); \
do \
if test -f "$$i".opt$(EXE); then \
$(INSTALL_PROG) "$$i.opt$(EXE)" "$(INSTALL_BINDIR)/$$i.opt$(EXE)"; \
(cd "$(INSTALL_BINDIR)/" && $(LN) "$$i.opt$(EXE)" "$$i$(EXE)"); \
fi; \
done
endif
# The preprocessor for asm generators
cvt_emit := cvt_emit$(EXE)
$(eval $(call PROGRAM_SYNONYM,cvt_emit))
$(cvt_emit): cvt_emit.cmo
$(CAMLC) $(LINKFLAGS) -o $@ $^
clean::
rm -f cvt_emit.ml cvt_emit cvt_emit.exe
beforedepend:: cvt_emit.ml
# Reading cmt files
ocamlcmt_objects= \
2018-07-26 08:27:00 -07:00
$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma \
\
ocamlcmt.cmo
# Reading cmt files
$(call byte_and_opt,ocamlcmt,$(ocamlcmt_objects),)
install::
if test -f ocamlcmt.opt$(EXE); then \
$(INSTALL_PROG)\
ocamlcmt.opt$(EXE) "$(INSTALL_BINDIR)/ocamlcmt$(EXE)"; \
else \
$(INSTALL_PROG) ocamlcmt$(EXE) "$(INSTALL_BINDIR)/ocamlcmt$(EXE)"; \
fi
# The bytecode disassembler
2018-07-23 05:19:41 -07:00
DUMPOBJ= \
2018-07-26 08:27:00 -07:00
$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma \
2018-07-23 05:19:41 -07:00
\
opnames.cmo dumpobj.cmo
2018-07-23 05:19:41 -07:00
$(call byte_and_opt,dumpobj,$(DUMPOBJ),)
make_opcodes := make_opcodes$(EXE)
$(eval $(call PROGRAM_SYNONYM,make_opcodes))
2016-09-11 08:02:05 -07:00
$(make_opcodes): make_opcodes.ml
$(CAMLC) $< -o $@
opnames.ml: $(ROOTDIR)/runtime/caml/instruct.h $(make_opcodes)
$(ROOTDIR)/runtime/ocamlrun$(EXE) $(make_opcodes) -opnames < $< > $@
clean::
rm -f opnames.ml make_opcodes make_opcodes.exe make_opcodes.ml
beforedepend:: opnames.ml
# Display info on compiled files
DEF_SYMBOL_PREFIX = '-Dsymbol_prefix=""'
ifeq "$(SYSTEM)" "macosx"
DEF_SYMBOL_PREFIX = '-Dsymbol_prefix="_"'
endif
ifeq "$(SYSTEM)" "cygwin"
DEF_SYMBOL_PREFIX = '-Dsymbol_prefix="_"'
endif
objinfo_helper$(EXE): objinfo_helper.$(O)
$(CC) $(BFD_LDFLAGS) $(OC_CFLAGS) $(OUTPUTEXE)$@ $< $(BFD_LDLIBS)
objinfo_helper.$(O): $(ROOTDIR)/runtime/caml/s.h
objinfo_helper.$(O): \
OC_CPPFLAGS += -I$(ROOTDIR)/runtime $(DEF_SYMBOL_PREFIX) $(BFD_CPPFLAGS)
2018-07-26 08:27:00 -07:00
OBJINFO=$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma \
$(ROOTDIR)/compilerlibs/ocamlmiddleend.cma \
objinfo.cmo
$(call byte_and_opt,ocamlobjinfo,$(OBJINFO),objinfo_helper$(EXE))
install::
$(INSTALL_PROG) \
makefiles: use 'install' instead of 'cp' in 'make install' targets I can observe weird performance bottlenecks on my machine caused by the use of 'cp' in the 'install' scripts of OCaml. When installing into a directory that is already populated by an existing installation, 'make install' can routinely take 10s on my machine¹. After this change it reliably takes 1.5s, independently of whether the destination is already populated or not. ¹: a brtfs filesystem on an old-ish SSD Why I care ---------- An extra 10s delay due to 'make install' can be noticeable in tight change-build-install-test feedback loops for a compiler change where we change the compiler, have a fast 'make world.opt' due to incremental builds, install the change and test it -- possibly after installing a couple opam packages, which can be fairly quick. Partial diagnosis ----------------- The performance issue seems to be caused by the fact that 'cp' (at least the GNU coreutils version), when the file already exists, replaces it by opening it in writeonly+truncate mode and writing the file content ('strace' shows that the delay is caused within an 'openat' call). In particular, using the --remove-destination option (which changes 'cp' to just remove the destination file before copying) removes the performance issue, but this option seems missing from the BSD/OSX 'cp' so it could cause portability issue. Change ------ The present commit rewrites the 'install' targets of all Makefiles to use the 'install' command instead. 'install' by default gives executable-like permission to the destination file, instead of reusing the source file's permissions, so we specify manually the permission modes, depending on whether the installed file is an executable (or dynamically-linked library) or just data (including other compiled object files). Testing ------- I checked manually that the permissions of the installed files are identical to the ones of the current 'cp'-using targets, except for some '.mli' file in middle_end which currently have +x bits enabled for no good reason. Remark: To test this, playing with the DESTDIR variable is very useful (this lets you install to a new directory (or the same as before) without having to re-run the configure script). I used the following, fairly slow shell script to collect permissions: for f in $(find $DESTDIR); do \ echo $(basename $f) $(ls -l $f | cut -d' ' -f1); \ done | sort Remark: it is important to run `sync` in-between 'make install' runs to avoid timing effects due to filesystem or disk caching strategies. I believe that this corresponds to the natural time delay (and unrelated disk activity) that would occur in realistic change-install-test feedback loops.
2018-03-28 08:46:34 -07:00
objinfo_helper$(EXE) "$(INSTALL_LIBDIR)/objinfo_helper$(EXE)"
2018-07-26 08:27:00 -07:00
primreq=$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma \
2018-07-23 05:19:41 -07:00
primreq.cmo
# Scan object files for required primitives
2018-07-23 05:19:41 -07:00
$(call byte_and_opt,primreq,$(primreq),)
2018-07-26 08:27:00 -07:00
LINTAPIDIFF=$(ROOTDIR)/compilerlibs/ocamlcommon.cmxa \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cmxa \
$(ROOTDIR)/compilerlibs/ocamlmiddleend.cmxa \
$(ROOTDIR)/otherlibs/str/str.cmxa \
lintapidiff.cmx
lintapidiff.opt$(EXE): INCLUDES+= -I $(ROOTDIR)/otherlibs/str
lintapidiff.opt$(EXE): $(LINTAPIDIFF)
2018-09-06 07:12:42 -07:00
$(CAMLOPT_CMD) $(LINKFLAGS) -I $(ROOTDIR) -o $@ $(LINTAPIDIFF)
clean::
rm -f -- lintapidiff.opt lintapidiff.opt.exe
rm -f lintapidiff.cm? lintapidiff.o lintapidiff.obj
clean::
rm -f "objinfo_helper" "objinfo_helper.manifest"
rm -f "objinfo_helper.exe" "objinfo_helper.exe.manifest"
# Eventlog metadata file
install::
$(INSTALL_DATA) \
eventlog_metadata \
"$(INSTALL_LIBDIR)"
# Copy a bytecode executable, stripping debug info
2018-07-26 08:27:00 -07:00
stripdebug=$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma \
stripdebug.cmo
$(call byte_and_opt,stripdebug,$(stripdebug),)
# Compare two bytecode executables
2018-07-26 08:27:00 -07:00
CMPBYT=$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma \
cmpbyt.cmo
$(call byte_and_opt,cmpbyt,$(CMPBYT),)
caml_tex_files := \
$(ROOTDIR)/compilerlibs/ocamlcommon.cma \
$(ROOTDIR)/compilerlibs/ocamlbytecomp.cma \
$(ROOTDIR)/compilerlibs/ocamltoplevel.cma \
$(ROOTDIR)/otherlibs/str/str.cma \
$(ROOTDIR)/otherlibs/$(UNIXLIB)/unix.cma \
caml_tex.ml
#Scan latex files, and run ocaml code examples
caml_tex := caml-tex$(EXE)
$(caml_tex): INCLUDES += $(addprefix -I $(ROOTDIR)/otherlibs/,str $(UNIXLIB))
$(caml_tex): $(caml_tex_files)
$(ROOTDIR)/runtime/ocamlrun$(EXE) $(ROOTDIR)/ocamlc$(EXE) -nostdlib \
2018-07-26 08:27:00 -07:00
-I $(ROOTDIR)/stdlib $(LINKFLAGS) -linkall \
-o $@ -no-alias-deps $^
# we need str and unix which depend on the bytecode version of other tools
# thus we delay building caml-tex to the opt.opt stage
ifneq "$(WITH_CAMLTEX)" ""
opt.opt: $(caml_tex)
endif
clean::
rm -f -- caml-tex caml-tex.exe caml_tex.cm?
# Common stuff
%.ml: %.mll
$(CAMLLEX) $(OCAMLLEX_FLAGS) $<
%.cmo: %.ml
$(CAMLC) -c $(COMPFLAGS) - $<
%.cmi: %.mli
$(CAMLC) -c $(COMPFLAGS) - $<
%.cmx: %.ml
$(CAMLOPT) $(COMPFLAGS) -c - $<
clean::
rm -f *.cmo *.cmi *.cma *.dll *.so *.lib *.a
CAMLDEP=$(BOOT_OCAMLC) -depend
DEPFLAGS=-slash
DEPINCLUDES=$(INCLUDES)
depend: beforedepend
$(CAMLDEP) $(DEPFLAGS) $(DEPINCLUDES) *.mli *.ml > .depend
.PHONY: clean install beforedepend depend
include .depend