From 4732d9452b2b351faef72a97f82d917a84025545 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 13 May 2024 16:38:13 +0800 Subject: [PATCH] refactor(build): Portable bootstrap in makefiles Avoid "target-specific variable values" (that looks like "target: variable=value") that works with GNU make only when building 'stage1flex'. A portable alternative is starting a sub-instance of 'make' with overridden variables (CC="$CC_FOR_BUILD" etc.). Make the bootstrap work even when EXEEXT and BUILD_EXEEXT might differ. The makefiles are designed that only the main instance of 'make' would handle the 'stage1bin/flex$(BUILD_EXEEXT)' so that no data contention can potentially occur. --- .gitignore | 1 + configure.ac | 4 +++ doc/.gitignore | 3 -- doc/local.mk | 44 +++++++++++++++++++++++------ src/local.mk | 75 ++++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 102 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index f3e3e84e0..c0690f1c7 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ Makefile /flex /flex.exe /libfl.la +/stage1bin/ /stage1flex /stage1flex.exe *.lo diff --git a/configure.ac b/configure.ac index dec7466c4..0eeaf38dd 100644 --- a/configure.ac +++ b/configure.ac @@ -127,6 +127,10 @@ AC_PATH_PROG([HELP2MAN], help2man, [\${top_srcdir}/build-aux/missing help2man]) AC_MSG_WARN(help2man: program not found: building man page will not work) ) +AM_CONDITIONAL([AUTO_BUILD_MAN_PAGE], + [test "$HELP2MAN" != "\${top_srcdir}/build-aux/missing help2man" && + { test "x$enable_bootstrap" = xyes || test "$cross_compiling" = no; }]) + AC_PATH_PROGS([TEXI2DVI], [gtexi2dvi texi2dvi], [\${top_srcdir}/build-aux/missing texi2dvi]) AS_IF([test "$TEXI2DVI" = "\${top_srcdir}/build-aux/missing texi2dvi"], AC_MSG_WARN(texi2dvi: program not found: building pdf version of manual will not work) diff --git a/doc/.gitignore b/doc/.gitignore index a685ba6e7..83b010866 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -25,6 +25,3 @@ flex.ps version.texi flex.html flex.t2p - -flex -flex.exe diff --git a/doc/local.mk b/doc/local.mk index 44d5c6a3a..dedcf6109 100644 --- a/doc/local.mk +++ b/doc/local.mk @@ -1,7 +1,7 @@ if CROSS -FLEX_FOR_DOC = stage1flex +FLEX_FOR_DOC = stage1bin/flex else -FLEX_FOR_DOC = flex$(EXEEXT) +FLEX_FOR_DOC = flex endif TEXI2DVI = @TEXI2DVI@ -I $(srcdir)/examples/manual/ @@ -33,16 +33,44 @@ CLEANFILES += \ doc/flex # Use a fixed program name, without extension (such as ".exe"), for man -# page generation. 'help2man' strips directory prefix ("./") from the -# usage string, but not file extensions. +# page generation. 'help2man' strips directory prefix from the usage +# string, but not file extensions. +# +# Note: Do NOT run the '$(FLEX_FOR_DOC)$(BUILD_EXEEXT)' target in +# another instance of 'make'! The scanner code also depends on the +# target and two 'make' instances may contest building the same file. -doc/flex.1: $(srcdir)/configure.ac $(srcdir)/src/cpp-flex.skl $(srcdir)/src/options.c $(srcdir)/src/options.h - $(MAKE) $(AM_MAKEFLAGS) $(FLEX_FOR_DOC) - $(INSTALL) -m 700 $(FLEX_FOR_DOC) doc/flex$(EXEEXT) +if AUTO_BUILD_MAN_PAGE +doc/flex.1: $(FLEX_FOR_DOC)$(BUILD_EXEEXT) + $(MKDIR_P) doc $(HELP2MAN) \ --name='$(PACKAGE_NAME)' \ --section=1 \ --source='The Flex Project' \ --manual='Programming' \ --output=$@ \ - doc/flex + ./$(FLEX_FOR_DOC) +else +doc/flex.1: $(srcdir)/configure.ac $(srcdir)/src/cpp-flex.skl $(srcdir)/src/options.c $(srcdir)/src/options.h + @option_text=''; \ + { test '$(FLEX_FOR_DOC)' = flex || \ + : $${option_text=" with --enable-bootstrap'"}; }; \ + echo "error: flex man page outdated; please install help2man and rerun 'configure'$${option_text}" 1>&2; + @false +endif + +distclean-local: distclean-local-doc + +# Clean the man page in the build directory only if it is not the +# source directory. + +distclean-local-doc: + test '$(srcdir)' = . || { \ + if mv -f $(srcdir)/doc/flex.1 $(srcdir)/doc/flex.1.tmp; then \ + s=0; else s=$$? || :; fi; \ + rm -f doc/flex.1 || :; \ + test "$$s" -ne 0 || \ + mv -f $(srcdir)/doc/flex.1.tmp $(srcdir)/doc/flex.1; \ + } + +.PHONY: distclean-local-doc diff --git a/src/local.mk b/src/local.mk index 44b0e6922..92e6ef13e 100644 --- a/src/local.mk +++ b/src/local.mk @@ -31,24 +31,67 @@ nodist_stage1flex_SOURCES = \ $(SKELINCLUDES) if CROSS -stage1flex_LDADD = + stage1flex_SOURCES += \ lib/malloc.c \ lib/realloc.c -stage1flex_LINK = $(LIBTOOL) --tag=CC --mode=link $(CC_FOR_BUILD) \ - $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -o $@ - -$(stage1flex_OBJECTS): CC=$(CC_FOR_BUILD) -$(stage1flex_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) -DUSE_CONFIG_FOR_BUILD -$(stage1flex_OBJECTS): CPP=$(CPP_FOR_BUILD) -$(stage1flex_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) -$(stage1flex_OBJECTS): LDFLAGS=$(LDFLAGS_FOR_BUILD) + +stage1flex_CPPFLAGS = -DUSE_CONFIG_FOR_BUILD $(AM_CPPFLAGS) + +# This also overrides AM_LIBTOOLFLAGS and AM_LDFLAGS +stage1flex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(LIBTOOLFLAGS) \ + --mode=link $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) \ + $(LDFLAGS_FOR_BUILD) -o $@ + +stage1flex_LDADD = + else + +stage1flex_CPPFLAGS = $(AM_CPPFLAGS) + +stage1flex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(CFLAGS) $(LDFLAGS) -o $@ + stage1flex_LDADD = $(LDADD) -stage1flex_LINK = $(LINK) -stage1flex_CFLAGS = $(AM_CFLAGS) + endif +# Suppress build warnings when compiling and linking stage1flex. +stage1flex_CFLAGS = + +# Override the 'stage1flex$(EXEEXT)' target automake would generate. +# If the compiler is not $(CC_FOR_BUILD) when this target is invoked, +# delete the object files that would be linked to 'stage1flex' to force +# rebuild all of them. + +stage1flex$(EXEEXT): $(stage1flex_OBJECTS) $(stage1flex_DEPENDENCIES) $(EXTRA_stage1flex_DEPENDENCIES) + @rm -f stage1flex$(EXEEXT) + @{ test 'x$(CC)' = 'x$(CC_FOR_BUILD)' && test 'x$(CPP)' = 'x$(CPP_FOR_BUILD)'; } || { \ + rm -f src/stage1flex-*.$(OBJEXT); \ + echo 'error: stage1flex must be built with a native compiler' 1>&2; \ + false; \ + } + $(AM_V_CCLD)$(stage1flex_LINK) $(stage1flex_OBJECTS) $(stage1flex_LDADD) $(LIBS) + +# The 'stage1bin/flex$(BUILD_EXEEXT)' target is not managed by +# automake. We use a different file name from the automake-managed +# 'stage1flex$(EXEEXT)' target to avoid clashing. + +stage1bin/flex$(BUILD_EXEEXT): + @if test 'x$(EXEEXT)' != 'x$(BUILD_EXEEXT)'; then rm -f stage1flex$(EXEEXT); else :; fi + @if test 'x$(OBJEXT)' != 'x$(BUILD_OBJEXT)'; then rm -f src/stage1flex-*.$(OBJEXT); else :; fi + $(MAKE) $(AM_MAKEFLAGS) \ + CC='$(CC_FOR_BUILD)' \ + CPP='$(CPP_FOR_BUILD)' \ + CFLAGS='$(CFLAGS_FOR_BUILD)' \ + CPPFLAGS='$(CPPFLAGS_FOR_BUILD)' \ + LDFLAGS='$(LDFLAGS_FOR_BUILD)' \ + EXEEXT='$(BUILD_EXEEXT)' \ + OBJEXT='$(BUILD_OBJEXT)' \ + stage1flex$(BUILD_EXEEXT) + $(MKDIR_P) stage1bin + $(INSTALL) -m 700 stage1flex$(BUILD_EXEEXT) $@ + flex_SOURCES = \ $(COMMON_SOURCES) @@ -108,11 +151,15 @@ EXTRA_DIST += \ MOSTLYCLEANFILES += \ $(SKELINCLUDES) \ + src/stage1flex-*.$(BUILD_OBJEXT) \ src/stage1scan.c \ src/stage2scan.c \ src/stage2compare -CLEANFILES += stage1flex$(EXEEXT) +CLEANFILES += \ + stage1bin/flex$(BUILD_EXEEXT) \ + stage1flex$(BUILD_EXEEXT) \ + stage1flex$(EXEEXT) SKELINCLUDES = \ src/cpp-flex.h \ @@ -137,8 +184,8 @@ src/go-flex.h: src/go-flex.skl src/mkskel.sh # The input and output file names are fixed for deterministic scanner # generation. If scan.l is not modified by builders, stage1scan.c should # be bit-identical to the scan.c pregenerated on release. -src/stage1scan.c: src/scan.l stage1flex$(EXEEXT) - ( cd $(srcdir)/src && $(abs_builddir)/stage1flex$(EXEEXT) \ +src/stage1scan.c: src/scan.l stage1bin/flex$(BUILD_EXEEXT) + ( cd $(srcdir)/src && $(abs_builddir)/stage1bin/flex$(BUILD_EXEEXT) \ $(AM_LFLAGS) $(LFLAGS) -o scan.c -t scan.l ) >$@ || \ { s=$$?; rm -f $@; exit $$s; }