mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
Here is an example of a failure with the previous dependency
calculation:
$ make -j5
make: Entering directory '/home/rjones/d/libguestfs/common/mlstdutils'
OCAMLOPT guestfs_config.cmx
CC libmlstdutils_a-dummy.o
OCAMLOPT libdir.cmx
OCAMLCMI stringMap.cmi
OCAMLCMI stringSet.cmi
OCAMLCMI guestfs_config.cmi
OCAMLCMI std_utils.cmi
OCAMLC guestfs_config.cmo
OCAMLC libdir.cmo
OCAMLC stringMap.cmo
OCAMLC stringSet.cmo
OCAMLOPT stringMap.cmx
OCAMLOPT stringSet.cmx
OCAMLOPT std_utils.cmx
OCAMLC std_utils.cmo
ocamlfind ocamlc -package str,unix -I . -a guestfs_config.cmo libdir.cmo stringMap.cmo stringSet.cmo std_utils.cmo -o mlstdutils.cma
ocamlfind ocamlopt -package str,unix -I . -a guestfs_config.cmx libdir.cmx stringMap.cmx stringSet.cmx std_utils.cmx -o mlstdutils.cmxa
AR libmlstdutils.a
File "_none_", line 1:
Error: Files std_utils.cmx and guestfs_config.cmx
make inconsistent assumptions over interface Guestfs_config
make: *** [Makefile:2523: mlstdutils.cmxa] Error 2
make: Leaving directory '/home/rjones/d/libguestfs/common/mlstdutils'
What seems to be happening is that there is a rule:
std_utils.cmx : guestfs_config.cmi guestfs_config.cmx [...]
In this case, make chose to build guestfs_config.cmx and
guestfs_config.cmi in parallel (see the first 5 rules above). However
building guestfs_config.cmx also creates guestfs_config.cmi
(implicitly - this is not expressed in make dependencies, and make
doesn't "know" that guestfs_config.cmi has already been created).
Unfortunately the OCaml compiler doesn't create output files
atomically. Worse than that, it creates an intermediate version of
the output file, reads it back and then creates the final version. It
seems if the build of std_utils.cmi reads this intermediate version.
In any case, Std_utils sees the wrong hash of the Guestfs_config
module.
The above only happens where we have a *.ml file without a
corresponding *.mli file. That is because if there is a *.mli file,
ocamldep generates slightly different dependencies:
guestfs_config.cmx [...] : guestfs_config.cmi guestfs_config.ml
std_utils.cmx : guestfs_config.cmi guestfs_config.cmx [...]
std_utils.cmx still depends on both files, but there is an extra rule
which ensures that guestfs_config.cmx isn't built in parallel with
guestfs_config.cmi.
I tested this change by running this command:
$ while ( rm common/mlstdutils/.depend; make -C common/mlstdutils/ clean && make -C common/mlstdutils/ ) >& /tmp/log; do echo -n .; done
Before the change it would fail after about 100 iterations. After the
change it ran for 10000s iterations and did not fail ever.
Updates commit 6d0ad49d5e.
111 lines
4.2 KiB
Makefile
111 lines
4.2 KiB
Makefile
# libguestfs
|
|
# Copyright (C) 2009-2017 Red Hat Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
# subdir-rules.mk should be included in every *subdirectory* Makefile.am.
|
|
|
|
include $(top_srcdir)/common-rules.mk
|
|
|
|
# Individual Makefile.am's should define generator_built if that
|
|
# subdirectory contains any files which are built by the generator.
|
|
# Set generator_built to the list of those files.
|
|
|
|
$(generator_built): $(top_builddir)/generator/stamp-generator
|
|
|
|
$(top_builddir)/generator/stamp-generator: $(top_builddir)/generator/generator
|
|
@if test -f $(top_builddir)/generator/Makefile; then \
|
|
$(MAKE) -C $(top_builddir)/generator stamp-generator; \
|
|
else \
|
|
echo "warning: Run 'make' at the top level to build $(generator_built)"; \
|
|
fi
|
|
|
|
# If this file doesn't exist, just print a warning and continue.
|
|
# During 'make distclean' we can end up deleting this file.
|
|
$(top_builddir)/generator/generator:
|
|
@if test -f $(top_builddir)/generator/Makefile; then \
|
|
$(MAKE) -C $(top_builddir)/generator generator; \
|
|
else \
|
|
echo "warning: Run 'make' at the top level to build $@"; \
|
|
fi
|
|
|
|
LOG_DRIVER = env $(SHELL) $(top_srcdir)/build-aux/guestfs-test-driver
|
|
|
|
# Rules for building OCaml objects.
|
|
# See also:
|
|
# guestfs-hacking(1) section "HOW OCAML PROGRAMS ARE COMPILED AND LINKED"
|
|
|
|
if !HAVE_OCAMLOPT
|
|
MLARCHIVE = cma
|
|
LINK_CUSTOM_OCAMLC_ONLY = -custom
|
|
BEST = c
|
|
else
|
|
MLARCHIVE = cmxa
|
|
BEST = opt
|
|
endif
|
|
|
|
# custom silent rules
|
|
guestfs_am_v_ocamlc = $(guestfs_am_v_ocamlc_@AM_V@)
|
|
guestfs_am_v_ocamlc_ = $(guestfs_am_v_ocamlc_@AM_DEFAULT_V@)
|
|
guestfs_am_v_ocamlc_0 = @echo " OCAMLC " $@;
|
|
guestfs_am_v_ocamlcmi= $(guestfs_am_v_ocamlcmi_@AM_V@)
|
|
guestfs_am_v_ocamlcmi_ = $(guestfs_am_v_ocamlcmi_@AM_DEFAULT_V@)
|
|
guestfs_am_v_ocamlcmi_0 = @echo " OCAMLCMI" $@;
|
|
guestfs_am_v_ocamlopt = $(guestfs_am_v_ocamlopt_@AM_V@)
|
|
guestfs_am_v_ocamlopt_ = $(guestfs_am_v_ocamlopt_@AM_DEFAULT_V@)
|
|
guestfs_am_v_ocamlopt_0 = @echo " OCAMLOPT" $@;
|
|
guestfs_am_v_javac = $(guestfs_am_v_javac_@AM_V@)
|
|
guestfs_am_v_javac_ = $(guestfs_am_v_javac_@AM_DEFAULT_V@)
|
|
guestfs_am_v_javac_0 = @echo " JAVAC " $@;
|
|
guestfs_am_v_erlc = $(guestfs_am_v_erlc_@AM_V@)
|
|
guestfs_am_v_erlc_ = $(guestfs_am_v_erlc_@AM_DEFAULT_V@)
|
|
guestfs_am_v_erlc_0 = @echo " ERLC " $@;
|
|
guestfs_am_v_podwrapper = $(guestfs_am_v_podwrapper_@AM_V@)
|
|
guestfs_am_v_podwrapper_ = $(guestfs_am_v_podwrapper_@AM_DEFAULT_V@)
|
|
guestfs_am_v_podwrapper_0 = @echo " POD " $@;
|
|
guestfs_am_v_jar = $(guestfs_am_v_jar_@AM_V@)
|
|
guestfs_am_v_jar_ = $(guestfs_am_v_jar_@AM_DEFAULT_V@)
|
|
guestfs_am_v_jar_0 = @echo " JAR " $@;
|
|
|
|
%.cmi: %.mli
|
|
$(guestfs_am_v_ocamlcmi)$(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
|
|
%.cmo: %.ml
|
|
$(guestfs_am_v_ocamlc)$(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
|
|
if HAVE_OCAMLOPT
|
|
%.cmx: %.ml
|
|
$(guestfs_am_v_ocamlopt)$(OCAMLFIND) ocamlopt $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
|
|
endif
|
|
|
|
# Test shell scripts should use '$TEST_FUNCTIONS' to get a predefined
|
|
# set of helper functions for running tests (see
|
|
# tests/test-functions.sh).
|
|
#
|
|
# Notes:
|
|
#
|
|
# (1) This is in fact a single command all on one line. The variables
|
|
# are evaluated in test-functions.sh.
|
|
#
|
|
# (2) We use absolute paths here and in test-functions.sh so that the
|
|
# test can change directory freely. But we also include the
|
|
# non-absolute values so they can be used by the test script itself.
|
|
export TEST_FUNCTIONS := \
|
|
source $(abs_top_srcdir)/tests/test-functions.sh \
|
|
abs_srcdir="$(abs_srcdir)" \
|
|
abs_builddir="$(abs_builddir)" \
|
|
top_srcdir="$(top_srcdir)" \
|
|
top_builddir="$(top_builddir)" \
|
|
abs_top_srcdir="$(abs_top_srcdir)" \
|
|
abs_top_builddir="$(abs_top_builddir)"
|