Skip to content

Support cross-building (without emulation) - #9445

Open
whitslack wants to merge 17 commits into
ElementsProject:masterfrom
whitslack:cross-build
Open

Support cross-building (without emulation)#9445
whitslack wants to merge 17 commits into
ElementsProject:masterfrom
whitslack:cross-build

Conversation

@whitslack

Copy link
Copy Markdown
Collaborator

This PR builds upon #8933 and rustyrussell/ccan#135 to bring full cross-building support to CLN.

The biggest user-visible changes are:

  • configure now has --host= and --build= options similar to Autoconf-based configure scripts.
  • The build products are placed in build/${HOST} by default, although this can be overridden by setting BUILDDIR. ($(HOST} is the value passed to --host= or else ${CC} -dumpmachine).
  • Cross-building is now possible without the need for QEMU or other CPU emulation to run the configurator tests and build-time tools.

Checklist

Before submitting the PR, ensure the following tasks are completed. If an item is not applicable to your PR, please mark it as checked:

  • The changelog has been updated in the relevant commit(s) according to the guidelines.
  • Tests have been added or modified to reflect the changes. (none needed)
  • Documentation has been reviewed and updated as needed.
  • Related issues have been listed and linked, including any that this PR closes. (none found)
  • Important All PRs must consider how to reverse any persistent changes for tools/lightning-downgrade (n/a)

It's not POSIX-compatible. Use printf instead.

Changelog-None
The code intends to pass "$DEFAULT_COPTFLAGS" and "$DEBUGBUILD" as arguments
$1 and $4 to default_cwarnflags(), but it had mistakenly doubled the double-
quotes, which would have caused the values of those variables to be subjected
to word splitting after substitution. Remove the extra double-quote marks.

Changelog-None
This was apparently a copy-paste mistake. The example link command line
shown by the 'show-flags' target shouldn't include $^ since 'show-flags'
has no prerequisites that are actually object files or libraries. The
inclusion of $^ was causing the example command line to contain
"config.vars", which makes no sense. Remove the $^.
Pass these flags variables when building configurator and tests.

Makefile now *prepends* its default CFLAGS, CPPFLAGS, and LDFLAGS to the
environment-supplied flags. This allows the user to override individual
flags by setting these variables through configure, without disturbing
all the rest of the flags that Makefile wants by default.

Changelog-None
Make predefines variables COMPILE.c and LINK.c, providing the default
commands for compiling and linking C programs:

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

Use these variables where appropriate.

A few points of interest:

* Using $(LINK.o) to link a C program is not correct, as it does not pass
  $(CFLAGS) to the linker driver. Passing $(CFLAGS) may be necessary for
  correct operation. For instance, -m32 can be specified in CFLAGS to build
  for a 32-bit ABI on a 64-bit-native system, and -flto can be specified in
  CFLAGS to enable link-time optimization. The linker driver needs to be
  told both of these in order to produce correct output.

* CFLAGS is not supposed to subsume CPPFLAGS. The latter are logically the
  flags for the C preprocessor, while the former are the flags for the C
  compiler. The standard COMPILE.c variable incorporates both sets of flags
  since it invokes both the preprocessor and the compiler with one command.
  The standard LINK.c variable also incorporates both since it can be used
  to preprocess, compile, and link a C program all in one shot. In its more
  typical usage (linking precompiled object files), the linker driver
  accepts but makes no use of any preprocessor flags supplied to it.

* CFLAGS logically shouldn't include any -D, -U, or -I options, as those are
  meant for the preprocessor and not the compiler. Move such flags to
  CPPFLAGS.

Changelog-None
It doesn't logically belong in CDEBUGFLAGS.

Changelog-None
Since we're linking a standalone program, we have to define main().

Changelog-None
libwally-core's headers directory is present in the include search path,
so its headers should not be included as
	#include <external/libwally-core/include/wally_script.h>
but rather as
	#include <wally_script.h>
.

This matters when building CLN against a system-installed libwally-core,
as the bundled copy in external/libwally-core might be absent.

Changelog-None
Rather than installing all build products monolithically, we can
leverage Make's timestamp testing to update only the installed targets
whose sources have been modified since the previous install.

* Rather than doing 'for' loops in the shell to install build products,
  use Make's 'foreach' and 'eval' functions to define an explicit rule
  for each individual installation target file. Then these targets can
  be listed as prerequisites of the phony 'install-*' targets.

* Specify the installation directories as order-only prerequisites of
  the targets that install into them. This way, Make will create these
  directories before installing into them only if they do not already
  exist.

* As a happy side effect of letting Make decide which installation
  targets need to be installed, the user can now disable installation of
  certain targets just by clearing the source list variables (e.g.,
  MANPAGES, DOC_DATA) on the 'make' command line. Previously, that would
  cause errors because Make would try to invoke $(INSTALL_DATA) with an
  empty list of source files.

* The Python plugins $(PY_PLUGINS) (of which there are currently none)
  are handled specially since their entire containing directory needs to
  be installed. For each distinct directory containing any files listed
  in $(PY_PLUGINS), define a rule that touches the directory whenever it
  is older than any listed file contained within it. This ensures that
  the whole plugin directory will be re-installed whenever any plugin
  source file within it has changed.

* Split the 'uninstall' target into 'uninstall-programs' and
  'uninstall-data' to mirror the 'install' target.

* Now that we have explicit lists of the installation target files,
  uninstallation is made simpler, becoming a single '$(RM) -r' command
  rather than numerous shell 'for' loops.

* When uninstalling, also remove the installation target directories if
  the uninstallation vacated them.

Changelog-None
Appropriate preprocessor and linker flags for libsodium, libsqlite3, and
libpq (PostgreSQL client) are normally looked up automagically via
pkg-config and pg_config, but advanced users may wish to override them.

* For libsodium, disable automagic detection when SODIUM_CFLAGS and/or
  SODIUM_LDLIBS is set in the environment (even to an empty string).

* For libsqlite3, disable automagic detection when SQLITE3_CFLAGS and/or
  SQLITE3_LDLIBS is set in the environment (even to an empty string).

* For libpq, disable automagic detection when POSTGRES_INCLUDE and/or
  POSTGRES_LDLIBS is set in the environment (even to an empty string).

Changelog-None
This allows building the same source tree for multiple different host
machines without needing to clean it in between, and it simplifies
cleanup of the build products down to a single subdirectory prune.

In the course of making this change, I also eliminated the big ugly list
of ccan object recipes in the top-level Makefile. Those object files are
now named similarly to their corresponding source files but are placed
beneath the build output directory, $(BUILDDIR), to avoid polluting the
source tree.

I also switched the 'obsclean' Make target to a double-colon target so
that the several Makefiles can all contribute commands to it.

Changelog-Changed: The build products are now placed into a separate directory tree.
 * --host=
       Specifies the tuple of the host machine on which the build
       products are intended to execute. Its value determines the
       default host-machine toolchain prefix. It defaults to empty.
 * --build=
       Specifies the tuple of the build machine on which the build
       itself executes. Its value determines the default build-machine
       toolchain prefix. It defaults to empty.
 * CC=
       Specifies the host-machine compiler driver. It defaults to
       "${HOST}-cc" if "${HOST}" is not empty, else "cc".
 * CFLAGS=
       Specifies the host-machine compiler flags. It defaults to empty.
       "-ffunction-sections" will be prepended if "${CC}" supports it.
 * CPPFLAGS=
       Specifies the host-machine preprocessor flags. It defaults to
       "-std=gnu11".
 * LDFLAGS=
       Specifies the host-machine linker flags. It defaults to empty.
       "-Wl,--gc-sections" will be prepended if "${CC}" supports it.
 * CC_FOR_BUILD=
       Specifies the build-machine compiler driver. It defaults to
       "${CC}" if "${BUILD}" == "${HOST}", else "${BUILD}-cc" if
       "${BUILD}" is not empty, else "cc".
 * CFLAGS_FOR_BUILD=
       Specifies the build-machine compiler flags. It defaults to
       "${CFLAGS}" if "${BUILD}" == "${HOST}", else "-O1".
       "-ffunction-sections" will be prepended if "${CC_FOR_BUILD}"
       supports it.
 * CPPFLAGS_FOR_BUILD=
       Specifies the build-machine preprocessor flags. It defaults to
       "${CPPFLAGS}" if "${BUILD}" == "${HOST}", else "-std=gnu11".
 * LDFLAGS_FOR_BUILD=
       Specifies the build-machine linker flags. It defaults to empty.
       "-Wl,--gc-sections" will be prepended if "${CC_FOR_BUILD}"
       supports it.

Build Configurator with "${CC_FOR_BUILD}" since it needs to run on the
build machine.

Changelog-Added: Configure has gained new options supporting cross-compilation.
We can test for the usability of headers and link libraries without
needing to execute the build products of the configurator tests. This is
crucial when cross-building, as the configurator build products may be
targeted at an architecture that the build machine cannot execute.

Changelog-None
headerversions as a compiled program does not play nicely with cross-
building, and we can't simply tweak the Makefiles to build it using
$(CC_FOR_BUILD), as the version of SQLite3 visible to the build-machine
toolchain may differ from the version visible to the host-machine toolchain.
Rewrite this build-time tool in POSIX shell so that it runs and emits version
information relevant to the host machine even when cross-building on a build
machine that may lack SQLite3 or may have a different version of it installed.

Changelog-None
Introduce some new Make variables:
 * BUILD: the build-machine tuple
 * AR_FOR_BUILD: ar for the build machine
 * CC_FOR_BUILD: C compiler for the build machine
 * CFLAGS_FOR_BUILD: compiler flags for the build machine
 * CPPFLAGS_FOR_BUILD: preprocessor flags for the build machine
 * LDFLAGS_FOR_BUILD: linker flags for the build machine
 * CROSS_BUILD: defined iff doing a cross-build
 * BUILDDIR_FOR_BUILD: build output directory for build machine

Introduce a new $(BUILDDIR_FOR_BUILD)/libccan.a static library, which
contains the code needed by build-time tools (namely, cdump_enumstr),
compiled for the build machine.

Don't regenerate doc/schemas/sql.json when doing a cross-build, as
$(BUILDDIR)/plugins/sql is not going to run on the build machine.

Eliminate references to QEMU in doc/…/getting-started/installation.md,
as emulation is no longer needed when configuring CLN for a cross-build.

Changelog-None
@whitslack
whitslack requested a review from Lagrang3 as a code owner August 24, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant