diff mbox series

[RFCv1,4/4] core: implement per-package SDK and target

Message ID 20171103160627.6468-5-thomas.petazzoni@free-electrons.com
State Superseded
Headers show
Series Per-package SDK and target directories | expand

Commit Message

Thomas Petazzoni Nov. 3, 2017, 4:06 p.m. UTC
This commit implemnts the core of the move to per-package SDK and
target directories. The main idea is that instead of having a global
output/host and output/target in which all packages install files, we
switch to per-package host and target folders, that only contain their
explicit dependencies.

There are two main benefits:

 - Packages will no longer discover dependencies that they do not
   explicitly indicate in their <pkg>_DEPENDENCIES variable.

 - We can support top-level parallel build properly, because a package
   only "sees" its own host directory and target directory, isolated
   from the build of other packages that can happen in parallel.

It works as follows:

 - A new output/per-package/ folder is created, which will contain one
   sub-folder per package, and inside it, a "host" folder and a
   "target" folder:

   output/per-package/busybox/target
   output/per-package/busybox/host
   output/per-package/host-fakeroot/target
   output/per-package/host-fakeroot/host

   This output/per-package/ folder is PER_PACKAGE_DIR, and each
   package defines <pkg>_TARGET_DIR, <pkg>_HOST_DIR and
   <pkg>_STAGING_DIR pointing to its own target, host and staging
   directories.

 - The <pkg>_TARGET_DIR, <pkg>_HOST_DIR and <pkg>_STAGING_DIR variable
   are passed as TARGET_DIR, HOST_DIR and STAGING_DIR to the various
   make targets used for the different build steps of the
   packages. Effectively this means that when a package references
   $(HOST_DIR), the value is in fact $(<pkg>_HOST_DIR).

 - Of course, packages have dependencies, so those dependencies must
   be installed in the per-package host and target folders before
   configuring the package. To do so, we simply rsync (using hard
   links to save space and time) the host and target folders of the
   direct dependencies of the package to the current package host and
   target folders. We only need to take care of direct dependencies
   (and not recursively all dependencies), because we accumulate into
   those per-package host and target folders the files installed by
   the dependencies.

 - We fix LD_LIBRARY_PATH to make it point to the current package host
   directory.

This is basically enough to make per-package SDK and target work. The
only gotcha is that at the end of the build, output/target and
output/host are empty, which means that:

 - The filesystem image creation code cannot work.

 - We don't have a SDK to build code outside of Buildroot.

In order to fix this, this commit extends the target-finalize step so
that it starts by populating output/target and output/host by
rsync-ing into them the target and host directories of all packages
listed in the $(PACKAGES) variable.

[Note: in fact, output/host is not completely empty, even though they
should be. It contains the following files:
output/host/
output/host/lib64
output/host/usr
output/host/share
output/host/share/buildroot
output/host/share/buildroot/Platform
output/host/share/buildroot/Platform/Buildroot.cmake
output/host/share/buildroot/toolchainfile.cmake
output/host/lib
Perhaps those files should be installed by some package instead,
perhaps the 'toolchain' package.]

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 Makefile               | 17 ++++++++++++++---
 package/pkg-generic.mk | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 3 deletions(-)

Comments

Arnout Vandecappelle Nov. 7, 2017, 10:50 p.m. UTC | #1
On 03-11-17 17:06, Thomas Petazzoni wrote:
> This commit implemnts the core of the move to per-package SDK and
> target directories. The main idea is that instead of having a global
> output/host and output/target in which all packages install files, we
> switch to per-package host and target folders, that only contain their
> explicit dependencies.
> 
> There are two main benefits:
> 
>  - Packages will no longer discover dependencies that they do not
>    explicitly indicate in their <pkg>_DEPENDENCIES variable.
> 
>  - We can support top-level parallel build properly, because a package
>    only "sees" its own host directory and target directory, isolated
>    from the build of other packages that can happen in parallel.
> 
> It works as follows:
> 
>  - A new output/per-package/ folder is created, which will contain one
>    sub-folder per package, and inside it, a "host" folder and a
>    "target" folder:
> 
>    output/per-package/busybox/target
>    output/per-package/busybox/host
>    output/per-package/host-fakeroot/target
>    output/per-package/host-fakeroot/host
> 
>    This output/per-package/ folder is PER_PACKAGE_DIR, and each
>    package defines <pkg>_TARGET_DIR, <pkg>_HOST_DIR and
>    <pkg>_STAGING_DIR pointing to its own target, host and staging
>    directories.
> 
>  - The <pkg>_TARGET_DIR, <pkg>_HOST_DIR and <pkg>_STAGING_DIR variable
>    are passed as TARGET_DIR, HOST_DIR and STAGING_DIR to the various
>    make targets used for the different build steps of the
>    packages. Effectively this means that when a package references
>    $(HOST_DIR), the value is in fact $(<pkg>_HOST_DIR).
> 
>  - Of course, packages have dependencies, so those dependencies must
>    be installed in the per-package host and target folders before
>    configuring the package. To do so, we simply rsync (using hard
>    links to save space and time) the host and target folders of the
>    direct dependencies of the package to the current package host and
>    target folders. We only need to take care of direct dependencies
>    (and not recursively all dependencies), because we accumulate into
>    those per-package host and target folders the files installed by
>    the dependencies.
> 
>  - We fix LD_LIBRARY_PATH to make it point to the current package host
>    directory.
> 
> This is basically enough to make per-package SDK and target work. The
> only gotcha is that at the end of the build, output/target and
> output/host are empty, which means that:
> 
>  - The filesystem image creation code cannot work.
> 
>  - We don't have a SDK to build code outside of Buildroot.
> 
> In order to fix this, this commit extends the target-finalize step so
> that it starts by populating output/target and output/host by
> rsync-ing into them the target and host directories of all packages
> listed in the $(PACKAGES) variable.

 It would have been good to mention somewhere in the commit log the problem of 2
packages writing the same file...

 Also, is there a good reason not to do the rsync at the end of package
installation? I don't know how robust rsync is against race conditions in
directory or hardlink creation... OK, so I did a test and it races pretty badly
:-) So, could you add this to the commit log? Like:

It is necessary to do this sequentially in the target-finalize step and not in
each package. Doing it in package installation means that it can be done in
parallel. In that case, there is a chance that two rsyncs are creating the same
hardlink or directory at the same time, which makes one of them fail.

> 
> [Note: in fact, output/host is not completely empty, even though they
> should be. It contains the following files:
> output/host/
> output/host/lib64
> output/host/usr
> output/host/share
> output/host/share/buildroot
> output/host/share/buildroot/Platform
> output/host/share/buildroot/Platform/Buildroot.cmake
> output/host/share/buildroot/toolchainfile.cmake
> output/host/lib
> Perhaps those files should be installed by some package instead,
> perhaps the 'toolchain' package.]

 If we go that way: perhaps the .cmake files should be installed by some package
like 'host-cmake-infra' that is added to the dependencies from cmake-package.

> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
>  Makefile               | 17 ++++++++++++++---
>  package/pkg-generic.mk | 37 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 51 insertions(+), 3 deletions(-)
> 
> diff --git a/Makefile b/Makefile
> index 5496273329..4e1ba62569 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -216,6 +216,8 @@ BR_GRAPH_OUT := $(or $(BR2_GRAPH_OUT),pdf)
>  BUILD_DIR := $(BASE_DIR)/build
>  BINARIES_DIR := $(BASE_DIR)/images
>  TARGET_DIR := $(BASE_DIR)/target
> +PER_PACKAGE_DIR := $(BASE_DIR)/per-package
> +
>  # initial definition so that 'make clean' works for most users, even without
>  # .config. HOST_DIR will be overwritten later when .config is included.
>  HOST_DIR := $(BASE_DIR)/host
> @@ -231,7 +233,7 @@ LEGAL_MANIFEST_CSV_HOST = $(LEGAL_INFO_DIR)/host-manifest.csv
>  LEGAL_WARNINGS = $(LEGAL_INFO_DIR)/.warnings
>  LEGAL_REPORT = $(LEGAL_INFO_DIR)/README
>  
> -export LD_LIBRARY_PATH = $(if $(PKG),$(HOST_DIR)/lib)
> +export LD_LIBRARY_PATH = $(if $(PKG),$($(PKG)_HOST_DIR)/lib)
>  
>  ################################################################################
>  #
> @@ -239,7 +241,7 @@ export LD_LIBRARY_PATH = $(if $(PKG),$(HOST_DIR)/lib)
>  # dependencies anywhere else
>  #
>  ################################################################################
> -$(BUILD_DIR) $(TARGET_DIR) $(HOST_DIR) $(BINARIES_DIR) $(LEGAL_INFO_DIR) $(REDIST_SOURCES_DIR_TARGET) $(REDIST_SOURCES_DIR_HOST):
> +$(BUILD_DIR) $(TARGET_DIR) $(HOST_DIR) $(BINARIES_DIR) $(LEGAL_INFO_DIR) $(REDIST_SOURCES_DIR_TARGET) $(REDIST_SOURCES_DIR_HOST) $(PER_PACKAGE_DIR):
>  	@mkdir -p $@
>  
>  BR2_CONFIG = $(CONFIG_DIR)/.config
> @@ -675,10 +677,19 @@ endef
>  TARGET_FINALIZE_HOOKS += PURGE_LOCALES
>  endif
>  
> +ALL_PACKAGES_TARGET_DIRS = $(foreach pkg,$(PACKAGES),$($(call UPPERCASE,$(pkg))_TARGET_DIR))
> +ALL_PACKAGES_HOST_DIRS   = $(foreach pkg,$(PACKAGES),$($(call UPPERCASE,$(pkg))_HOST_DIR))
> +
>  $(TARGETS_ROOTFS): target-finalize
>  
>  .PHONY: target-finalize
>  target-finalize: $(PACKAGES)
> +	@$(call MESSAGE,"Creating global target directory")
> +	$(foreach dir,$(ALL_PACKAGES_TARGET_DIRS),\
> +		rsync -au --link-dest=$(dir) $(dir) $(TARGET_DIR)$(sep))

 Why -u? Since we're not actually copying anything, there is no reason to not
overwrite files that already exist, right?

 Also, shouldn't there be a / behind $(dir)? Otherwise, if TARGET_DIR exists
already, rsync will create $(dir) as a subdirectory of $(TARGET_DIR), no?


> +	@$(call MESSAGE,"Creating global host directory")
> +	$(foreach dir,$(ALL_PACKAGES_HOST_DIRS),\
> +		rsync -au --link-dest=$(dir) $(dir) $(HOST_DIR)$(sep))
>  	@$(call MESSAGE,"Finalizing target directory")
>  	$(TOPDIR)/support/scripts/fix-rpath host
>  	$(foreach hook,$(TARGET_FINALIZE_HOOKS),$($(hook))$(sep))
> @@ -972,7 +983,7 @@ printvars:
>  clean:
>  	rm -rf $(TARGET_DIR) $(BINARIES_DIR) $(HOST_DIR) \
>  		$(BUILD_DIR) $(BASE_DIR)/staging \
> -		$(LEGAL_INFO_DIR) $(GRAPHS_DIR)
> +		$(LEGAL_INFO_DIR) $(GRAPHS_DIR) $(PER_PACKAGE_DIR)
>  
>  .PHONY: distclean
>  distclean: clean
> diff --git a/package/pkg-generic.mk b/package/pkg-generic.mk
> index 82f8c06821..aee4011f63 100644
> --- a/package/pkg-generic.mk
> +++ b/package/pkg-generic.mk
> @@ -158,6 +158,7 @@ $(BUILD_DIR)/%/.stamp_actual_downloaded:
>  $(BUILD_DIR)/%/.stamp_extracted:
>  	@$(call step_start,extract)
>  	@$(call MESSAGE,"Extracting")
> +	@mkdir -p $(HOST_DIR) $(TARGET_DIR) $(STAGING_DIR)

 Why do you create them here? It would make more sense to do it just before the
rsync, no?

 Given the possible host-tar and host-lzip dependencies, the rsync should be
done here already, not in the configure step. Ouch, that's not good, because the
dependencies are only evaluated for the configure step... There are
PATCH_DEPENDENCIES, but those are only before patch. Well, I guess that's a
reason to keep host-tar and host-lzip as DEPENDENCIES_HOST_PREREQ :-)

>  	$(foreach hook,$($(PKG)_PRE_EXTRACT_HOOKS),$(call $(hook))$(sep))
>  	$(Q)mkdir -p $(@D)
>  	$($(PKG)_EXTRACT_CMDS)
> @@ -215,6 +216,10 @@ $(foreach dir,$(call qstrip,$(BR2_GLOBAL_PATCH_DIR)),\
>  $(BUILD_DIR)/%/.stamp_configured:
>  	@$(call step_start,configure)
>  	@$(call MESSAGE,"Configuring")
> +	$(foreach dir,$($(PKG)_FINAL_DEPENDENCIES_HOST_DIRS),\
> +		rsync -au --link-dest=$(dir) $(dir) $(HOST_DIR)$(sep))

 Shouldn't there be a $Q somewhere?

> +	$(foreach dir,$($(PKG)_FINAL_DEPENDENCIES_TARGET_DIRS),\
> +		rsync -au --link-dest=$(dir) $(dir) $(TARGET_DIR)$(sep))
>  	$(foreach hook,$($(PKG)_PRE_CONFIGURE_HOOKS),$(call $(hook))$(sep))
>  	$($(PKG)_CONFIGURE_CMDS)
>  	$(foreach hook,$($(PKG)_POST_CONFIGURE_HOOKS),$(call $(hook))$(sep))
> @@ -409,6 +414,10 @@ $(2)_NAME			=  $(1)
>  $(2)_RAWNAME			=  $$(patsubst host-%,%,$(1))
>  $(2)_PKGDIR			=  $(pkgdir)
>  
> +$(2)_TARGET_DIR		= $$(PER_PACKAGE_DIR)/$(1)/target/

 Ah, here is the trailing slash, that explains why it worked. I don't really
like it when you have to rely on a trailing slash in the variable definition...

> +$(2)_HOST_DIR		= $$(PER_PACKAGE_DIR)/$(1)/host/
> +$(2)_STAGING_DIR	= $$(PER_PACKAGE_DIR)/$(1)/host/$(STAGING_SUBDIR)

 I hate adding more per-package variables. Can't you use the expanded values,
substituting $($(PKG)_NAME) for $(1)? Or maybe even better, change the
definition of TARGET_DIR etc:

TARGET_DIR = $(if
$(PKG),$(PER_PACKAGE_DIR)/$($(PKG)_RAWNAME)/target,$(BASE_DIR)/target)

 That way you also don't need to pass those variables to each individual stamp rule.

> +
>  # Keep the package version that may contain forward slashes in the _DL_VERSION
>  # variable, then replace all forward slashes ('/') by underscores ('_') to
>  # sanitize the package version that is used in paths, directory and file names.
> @@ -561,6 +570,13 @@ $(2)_FINAL_DEPENDENCIES = $$(sort $$($(2)_DEPENDENCIES))
>  $(2)_FINAL_PATCH_DEPENDENCIES = $$(sort $$($(2)_PATCH_DEPENDENCIES))
>  $(2)_FINAL_ALL_DEPENDENCIES = $$(sort $$($(2)_FINAL_DEPENDENCIES) $$($(2)_FINAL_PATCH_DEPENDENCIES))
>  
> +$(2)_FINAL_DEPENDENCIES_HOST_DIRS = \
> +	$$(foreach dep,$$($(2)_FINAL_DEPENDENCIES),\
> +		$$($$(call UPPERCASE,$$(dep))_HOST_DIR))
> +$(2)_FINAL_DEPENDENCIES_TARGET_DIRS = \
> +	$$(foreach dep,$$($(2)_FINAL_DEPENDENCIES),\
> +		$$($$(call UPPERCASE,$$(dep))_TARGET_DIR))

 I may be overengineering things, but perhaps things can be simplified as
follows (with the definition of TARGET_DIR etc as above):

define RSYNC_HOST_DIRS
	$(foreach pkg,$(1),\
		$(Q)dir=$(PER_PACKAGE_DIR)/$(pkg)/host;\
		rsync -au --link-dest=$$dir $$dir/ $(HOST_DIR)$(sep))
endef

define RSYNC_TARGET_DIRS
	$(foreach pkg,$(1),\
		$(Q)dir=$(PER_PACKAGE_DIR)/$(pkg)/target;\
		rsync -au --link-dest=$$dir $$dir/ $(TARGET_DIR)$(sep))
endef

(as always, completely untested). These macros can be used both in the config
stamp rule and in the finalize rule. I even considered leaving out the argument
and instead defaulting $(1) to
$(if $(PKG),$($(PKG)_FINAL_DEPENDENCIES),$(PACKAGES))
but that probably just makes things harder to understand.


 Otherwise looks good to me :-P

 Regards,
 Arnout

> +
>  $(2)_INSTALL_STAGING		?= NO
>  $(2)_INSTALL_IMAGES		?= NO
>  $(2)_INSTALL_TARGET		?= YES
> @@ -789,17 +805,38 @@ $(1)-reconfigure:	$(1)-clean-for-reconfigure $(1)
>  # define the PKG variable for all targets, containing the
>  # uppercase package variable prefix
>  $$($(2)_TARGET_INSTALL_TARGET):		PKG=$(2)
> +$$($(2)_TARGET_INSTALL_TARGET):		HOST_DIR=$$($(2)_HOST_DIR)
> +$$($(2)_TARGET_INSTALL_TARGET):		STAGING_DIR=$$($(2)_STAGING_DIR)
> +$$($(2)_TARGET_INSTALL_TARGET):		TARGET_DIR=$$($(2)_TARGET_DIR)
>  $$($(2)_TARGET_INSTALL_STAGING):	PKG=$(2)
> +$$($(2)_TARGET_INSTALL_STAGING):	HOST_DIR=$$($(2)_HOST_DIR)
> +$$($(2)_TARGET_INSTALL_STAGING):	STAGING_DIR=$$($(2)_STAGING_DIR)
> +$$($(2)_TARGET_INSTALL_STAGING):	TARGET_DIR=$$($(2)_TARGET_DIR)
>  $$($(2)_TARGET_INSTALL_IMAGES):		PKG=$(2)
> +$$($(2)_TARGET_INSTALL_IMAGES):		HOST_DIR=$$($(2)_HOST_DIR)
> +$$($(2)_TARGET_INSTALL_IMAGES):		STAGING_DIR=$$($(2)_STAGING_DIR)
> +$$($(2)_TARGET_INSTALL_IMAGES):		TARGET_DIR=$$($(2)_TARGET_DIR)
>  $$($(2)_TARGET_INSTALL_HOST):		PKG=$(2)
> +$$($(2)_TARGET_INSTALL_HOST):		HOST_DIR=$$($(2)_HOST_DIR)
> +$$($(2)_TARGET_INSTALL_HOST):		STAGING_DIR=$$($(2)_STAGING_DIR)
> +$$($(2)_TARGET_INSTALL_HOST):		TARGET_DIR=$$($(2)_TARGET_DIR)
>  $$($(2)_TARGET_BUILD):			PKG=$(2)
> +$$($(2)_TARGET_BUILD):			HOST_DIR=$$($(2)_HOST_DIR)
> +$$($(2)_TARGET_BUILD):			STAGING_DIR=$$($(2)_STAGING_DIR)
> +$$($(2)_TARGET_BUILD):			TARGET_DIR=$$($(2)_TARGET_DIR)
>  $$($(2)_TARGET_CONFIGURE):		PKG=$(2)
> +$$($(2)_TARGET_CONFIGURE):		HOST_DIR=$$($(2)_HOST_DIR)
> +$$($(2)_TARGET_CONFIGURE):		STAGING_DIR=$$($(2)_STAGING_DIR)
> +$$($(2)_TARGET_CONFIGURE):		TARGET_DIR=$$($(2)_TARGET_DIR)
>  $$($(2)_TARGET_RSYNC):			SRCDIR=$$($(2)_OVERRIDE_SRCDIR)
>  $$($(2)_TARGET_RSYNC):			PKG=$(2)
>  $$($(2)_TARGET_PATCH):			PKG=$(2)
>  $$($(2)_TARGET_PATCH):			RAWNAME=$$(patsubst host-%,%,$(1))
>  $$($(2)_TARGET_PATCH):			PKGDIR=$(pkgdir)
>  $$($(2)_TARGET_EXTRACT):		PKG=$(2)
> +$$($(2)_TARGET_EXTRACT):		HOST_DIR=$$($(2)_HOST_DIR)
> +$$($(2)_TARGET_EXTRACT):		STAGING_DIR=$$($(2)_STAGING_DIR)
> +$$($(2)_TARGET_EXTRACT):		TARGET_DIR=$$($(2)_TARGET_DIR)
>  $$($(2)_TARGET_SOURCE):			PKG=$(2)
>  $$($(2)_TARGET_SOURCE):			PKGDIR=$(pkgdir)
>  $$($(2)_TARGET_ACTUAL_SOURCE):		PKG=$(2)
>
Thomas Petazzoni Nov. 7, 2017, 11:11 p.m. UTC | #2
Hello,

Thanks a lot for the suggestions, I'll take them into account for the
next version. Just a few comments/questions below.

On Tue, 7 Nov 2017 23:50:52 +0100, Arnout Vandecappelle wrote:

> > In order to fix this, this commit extends the target-finalize step so
> > that it starts by populating output/target and output/host by
> > rsync-ing into them the target and host directories of all packages
> > listed in the $(PACKAGES) variable.  
> 
>  It would have been good to mention somewhere in the commit log the problem of 2
> packages writing the same file...

True. I believe the work Yann has done to detect packages overwriting
files installed by other packages is definitely a pre-requisite for
this work, so I was kind of assuming that the "packages don't overwrite
each other" would already be a rule by the time my per-package SDK
patches get merged. But I should make this explicit in the cover letter
at least.

>  Also, is there a good reason not to do the rsync at the end of package
> installation? I don't know how robust rsync is against race conditions in
> directory or hardlink creation... OK, so I did a test and it races pretty badly
> :-) So, could you add this to the commit log? Like:
> 
> It is necessary to do this sequentially in the target-finalize step and not in
> each package. Doing it in package installation means that it can be done in
> parallel. In that case, there is a chance that two rsyncs are creating the same
> hardlink or directory at the same time, which makes one of them fail.

I didn't think at all about doing this in parallel. To me, the creation
of the final/common HOST_DIR/TARGET_DIR/STAGING_DIR is not at
all something that needs to be done while the build is on-going. And I
could go even further: unless you call "make sdk", we technically don't
really need to create the final/common HOST_DIR/STAGING_DIR. Only a
TARGET_DIR is needed, and perhaps even we could avoid a global one, and
instead have a per-filesystem image one, in order to avoid the parallel
filesystem image creation problem.

> > [Note: in fact, output/host is not completely empty, even though they
> > should be. It contains the following files:
> > output/host/
> > output/host/lib64
> > output/host/usr
> > output/host/share
> > output/host/share/buildroot
> > output/host/share/buildroot/Platform
> > output/host/share/buildroot/Platform/Buildroot.cmake
> > output/host/share/buildroot/toolchainfile.cmake
> > output/host/lib
> > Perhaps those files should be installed by some package instead,
> > perhaps the 'toolchain' package.]  
> 
>  If we go that way: perhaps the .cmake files should be installed by some package
> like 'host-cmake-infra' that is added to the dependencies from cmake-package.

That was my thinking: perhaps those files should all be installed by
packages.

> >  .PHONY: target-finalize
> >  target-finalize: $(PACKAGES)
> > +	@$(call MESSAGE,"Creating global target directory")
> > +	$(foreach dir,$(ALL_PACKAGES_TARGET_DIRS),\
> > +		rsync -au --link-dest=$(dir) $(dir) $(TARGET_DIR)$(sep))  
> 
>  Why -u? Since we're not actually copying anything, there is no reason to not
> overwrite files that already exist, right?

I just borrowed parts of Gustavo patches, including those rsync
invocations. Indeed -u is not needed.

>  Also, shouldn't there be a / behind $(dir)? Otherwise, if TARGET_DIR exists
> already, rsync will create $(dir) as a subdirectory of $(TARGET_DIR), no?

I'll fix.

> > diff --git a/package/pkg-generic.mk b/package/pkg-generic.mk
> > index 82f8c06821..aee4011f63 100644
> > --- a/package/pkg-generic.mk
> > +++ b/package/pkg-generic.mk
> > @@ -158,6 +158,7 @@ $(BUILD_DIR)/%/.stamp_actual_downloaded:
> >  $(BUILD_DIR)/%/.stamp_extracted:
> >  	@$(call step_start,extract)
> >  	@$(call MESSAGE,"Extracting")
> > +	@mkdir -p $(HOST_DIR) $(TARGET_DIR) $(STAGING_DIR)  
> 
>  Why do you create them here? It would make more sense to do it just before the
> rsync, no?

I don't remember, I'll have to check again. I don't immediately see a
reason why they would be needed before the rsync.

>  Given the possible host-tar and host-lzip dependencies, the rsync should be
> done here already, not in the configure step. Ouch, that's not good, because the
> dependencies are only evaluated for the configure step... There are
> PATCH_DEPENDENCIES, but those are only before patch. Well, I guess that's a
> reason to keep host-tar and host-lzip as DEPENDENCIES_HOST_PREREQ :-)

These are all topics that I haven't looked at yet. So thanks for
pointing them out. I should probably collect a TODO list on per-package
SDK to not forget about all the things people have mentioned as things
to look at.

> > +	$(foreach dir,$($(PKG)_FINAL_DEPENDENCIES_HOST_DIRS),\
> > +		rsync -au --link-dest=$(dir) $(dir) $(HOST_DIR)$(sep))  
> 
>  Shouldn't there be a $Q somewhere?

Yes. Seeing the rsync commands was useful for debugging, as you can
imagine :)


> > +$(2)_TARGET_DIR		= $$(PER_PACKAGE_DIR)/$(1)/target/  
> 
>  Ah, here is the trailing slash, that explains why it worked. I don't really
> like it when you have to rely on a trailing slash in the variable definition...

OK.

> 
> > +$(2)_HOST_DIR		= $$(PER_PACKAGE_DIR)/$(1)/host/
> > +$(2)_STAGING_DIR	= $$(PER_PACKAGE_DIR)/$(1)/host/$(STAGING_SUBDIR)  
> 
>  I hate adding more per-package variables. Can't you use the expanded values,
> substituting $($(PKG)_NAME) for $(1)? Or maybe even better, change the
> definition of TARGET_DIR etc:
> 
> TARGET_DIR = $(if
> $(PKG),$(PER_PACKAGE_DIR)/$($(PKG)_RAWNAME)/target,$(BASE_DIR)/target)
> 
>  That way you also don't need to pass those variables to each individual stamp rule.

I didn't think about this, sounds like a good idea, I'll try that.

However, I'm actually planning to drop entirely having TARGET_DIR
defined outside of package rules. What I want to have ultimately is:

 - TARGET_DIR points to the current package per-package target
   directory. If we're outside of a package rule, TARGET_DIR has no
   value (or possibly a bogus value such as /this/is/a/bogus/thing/).
   Indeed, we really don't want things to directly install/mess with
   the global/common target directory.

 - Have something like COMMON_TARGET_DIR that points to
   $(BASE_DIR)/target, and we carefully change the places that should
   access the COMMON_TARGET_DIR (i.e mainly target-finalize and some
   filesystem generation stuff).

> >  # Keep the package version that may contain forward slashes in the _DL_VERSION
> >  # variable, then replace all forward slashes ('/') by underscores ('_') to
> >  # sanitize the package version that is used in paths, directory and file names.
> > @@ -561,6 +570,13 @@ $(2)_FINAL_DEPENDENCIES = $$(sort $$($(2)_DEPENDENCIES))
> >  $(2)_FINAL_PATCH_DEPENDENCIES = $$(sort $$($(2)_PATCH_DEPENDENCIES))
> >  $(2)_FINAL_ALL_DEPENDENCIES = $$(sort $$($(2)_FINAL_DEPENDENCIES) $$($(2)_FINAL_PATCH_DEPENDENCIES))
> >  
> > +$(2)_FINAL_DEPENDENCIES_HOST_DIRS = \
> > +	$$(foreach dep,$$($(2)_FINAL_DEPENDENCIES),\
> > +		$$($$(call UPPERCASE,$$(dep))_HOST_DIR))
> > +$(2)_FINAL_DEPENDENCIES_TARGET_DIRS = \
> > +	$$(foreach dep,$$($(2)_FINAL_DEPENDENCIES),\
> > +		$$($$(call UPPERCASE,$$(dep))_TARGET_DIR))  
> 
>  I may be overengineering things, but perhaps things can be simplified as
> follows (with the definition of TARGET_DIR etc as above):
> 
> define RSYNC_HOST_DIRS
> 	$(foreach pkg,$(1),\
> 		$(Q)dir=$(PER_PACKAGE_DIR)/$(pkg)/host;\
> 		rsync -au --link-dest=$$dir $$dir/ $(HOST_DIR)$(sep))
> endef
> 
> define RSYNC_TARGET_DIRS
> 	$(foreach pkg,$(1),\
> 		$(Q)dir=$(PER_PACKAGE_DIR)/$(pkg)/target;\
> 		rsync -au --link-dest=$$dir $$dir/ $(TARGET_DIR)$(sep))
> endef
> 
> (as always, completely untested). These macros can be used both in the config
> stamp rule and in the finalize rule. I even considered leaving out the argument
> and instead defaulting $(1) to
> $(if $(PKG),$($(PKG)_FINAL_DEPENDENCIES),$(PACKAGES))
> but that probably just makes things harder to understand.

I'll think about this suggestion.

Thanks again for the detailed review.

Thomas
Arnout Vandecappelle Nov. 7, 2017, 11:57 p.m. UTC | #3
On 08-11-17 00:11, Thomas Petazzoni wrote:
> Hello,
> 
> Thanks a lot for the suggestions, I'll take them into account for the
> next version. Just a few comments/questions below.
> 
> On Tue, 7 Nov 2017 23:50:52 +0100, Arnout Vandecappelle wrote:

[snip]
>>  Also, is there a good reason not to do the rsync at the end of package
>> installation? I don't know how robust rsync is against race conditions in
>> directory or hardlink creation... OK, so I did a test and it races pretty badly
>> :-) So, could you add this to the commit log? Like:
>>
>> It is necessary to do this sequentially in the target-finalize step and not in
>> each package. Doing it in package installation means that it can be done in
>> parallel. In that case, there is a chance that two rsyncs are creating the same
>> hardlink or directory at the same time, which makes one of them fail.
> 
> I didn't think at all about doing this in parallel. 

 The point is not that you may want to do it in parallel. The point is that it
is natural to do something that needs to be done for each package at the time
you build that package. Doing stuff in finalize is always a bit weird.

> To me, the creation
> of the final/common HOST_DIR/TARGET_DIR/STAGING_DIR is not at
> all something that needs to be done while the build is on-going. And I
> could go even further: unless you call "make sdk", we technically don't
> really need to create the final/common HOST_DIR/STAGING_DIR.

 Absolutely true, it would make sense to do that in 'make sdk'.

> Only a
> TARGET_DIR is needed, and perhaps even we could avoid a global one, and
> instead have a per-filesystem image one, in order to avoid the parallel
> filesystem image creation problem.

 That's a great idea!

[snip]
>>> diff --git a/package/pkg-generic.mk b/package/pkg-generic.mk
>>> index 82f8c06821..aee4011f63 100644
>>> --- a/package/pkg-generic.mk
>>> +++ b/package/pkg-generic.mk
>>> @@ -158,6 +158,7 @@ $(BUILD_DIR)/%/.stamp_actual_downloaded:
>>>  $(BUILD_DIR)/%/.stamp_extracted:
>>>  	@$(call step_start,extract)
>>>  	@$(call MESSAGE,"Extracting")
>>> +	@mkdir -p $(HOST_DIR) $(TARGET_DIR) $(STAGING_DIR)  
>>
>>  Why do you create them here? It would make more sense to do it just before the
>> rsync, no?
> 
> I don't remember, I'll have to check again. I don't immediately see a
> reason why they would be needed before the rsync.

 A package without any dependencies will not do the rsync, and I do think you
want the directories to exist. Or maybe not, I'm not sure... Actually, shouldn't
a skeleton be installed there as well?


>>  Given the possible host-tar and host-lzip dependencies, the rsync should be
>> done here already, not in the configure step. Ouch, that's not good, because the
>> dependencies are only evaluated for the configure step... There are
>> PATCH_DEPENDENCIES, but those are only before patch. Well, I guess that's a
>> reason to keep host-tar and host-lzip as DEPENDENCIES_HOST_PREREQ :-)
> 
> These are all topics that I haven't looked at yet. So thanks for
> pointing them out. I should probably collect a TODO list on per-package
> SDK to not forget about all the things people have mentioned as things
> to look at.

 Yeah, a skeleton HOST_DIR that every package depends on would simplify that,
perhaps.

[snip]
>>  I hate adding more per-package variables. Can't you use the expanded values,
>> substituting $($(PKG)_NAME) for $(1)? Or maybe even better, change the
>> definition of TARGET_DIR etc:
>>
>> TARGET_DIR = $(if
>> $(PKG),$(PER_PACKAGE_DIR)/$($(PKG)_RAWNAME)/target,$(BASE_DIR)/target)
>>
>>  That way you also don't need to pass those variables to each individual stamp rule.
> 
> I didn't think about this, sounds like a good idea, I'll try that.
> 
> However, I'm actually planning to drop entirely having TARGET_DIR
> defined outside of package rules. What I want to have ultimately is:
> 
>  - TARGET_DIR points to the current package per-package target
>    directory. If we're outside of a package rule, TARGET_DIR has no
>    value (or possibly a bogus value such as /this/is/a/bogus/thing/).
>    Indeed, we really don't want things to directly install/mess with
>    the global/common target directory.

 bogus thing, good plan!

> 
>  - Have something like COMMON_TARGET_DIR that points to
>    $(BASE_DIR)/target, and we carefully change the places that should
>    access the COMMON_TARGET_DIR (i.e mainly target-finalize and some
>    filesystem generation stuff).

 Ack yes, sounds good.


 Regards,
 Arnout


[snip]
Thomas Petazzoni Nov. 24, 2017, 2:43 p.m. UTC | #4
Hello,

A few more answers/topics I wanted to discuss.

On Tue, 7 Nov 2017 23:50:52 +0100, Arnout Vandecappelle wrote:

>  Given the possible host-tar and host-lzip dependencies, the rsync
> should be done here already, not in the configure step. Ouch, that's
> not good, because the dependencies are only evaluated for the
> configure step... There are PATCH_DEPENDENCIES, but those are only
> before patch. Well, I guess that's a reason to keep host-tar and
> host-lzip as DEPENDENCIES_HOST_PREREQ :-)

Here is how I'm thinking of solving the problem:

 - Next to <pkg>_PATCH_DEPENDENCIES, introduce the concept of
   <pkg>_EXTRACT_DEPENDENCIES.

 - If there is no suitable tar in the system, then host-tar would be
   added to <pkg>_EXTRACT_DEPENDENCIES of all packages, except host-tar
   itself.

 - If the package needs lzip and there is no lzip available on the
   system, host-lzip is added to <pkg>_EXTRACT_DEPENDENCIES.

 - If ccache support is enabled, host-ccache is added to
   <pkg>_DEPENDENCIES of all packages, except host-tar, which is built
   with HOSTCC_NOCCACHE.

 - At the beginning of the extract step of a package, we rsync to its
   per-package target/host directories the per-package target/host dirs
   of the packages listed in its <pkg>_EXTRACT_DEPENDENCIES variable.

 - At the beginning of the patch step of a package, we rsync to its
   per-package target/host directories the per-package target/host dirs
   of the packages listed in its <pkg>_PATCH_DEPENDENCIES variable.

Thoughts?

> > +$(2)_HOST_DIR		= $$(PER_PACKAGE_DIR)/$(1)/host/
> > +$(2)_STAGING_DIR	= $$(PER_PACKAGE_DIR)/$(1)/host/$(STAGING_SUBDIR)  
> 
>  I hate adding more per-package variables. Can't you use the expanded values,
> substituting $($(PKG)_NAME) for $(1)? Or maybe even better, change the
> definition of TARGET_DIR etc:
> 
> TARGET_DIR = $(if
> $(PKG),$(PER_PACKAGE_DIR)/$($(PKG)_RAWNAME)/target,$(BASE_DIR)/target)
> 
>  That way you also don't need to pass those variables to each individual stamp rule.

I've changed this in my new iteration. The only drawback is that since
we no longer have those shortcut variables, the foreach loops building
the per-package directories, and building the final global directories
are a bit less readable:

Per-package host/target creation:

	$(foreach pkg,$($(PKG)_FINAL_DEPENDENCIES),\
		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/host/ \
		$(PER_PACKAGE_DIR)/$(pkg)/host/ \
		$(HOST_DIR)$(sep))
	$(foreach pkg,$($(PKG)_FINAL_DEPENDENCIES),\
		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/target/ \
		$(PER_PACKAGE_DIR)/$(pkg)/target/ \
		$(TARGET_DIR)$(sep))

Global host/target creation in target-finalize:

	$(foreach pkg,$(PACKAGES),\
		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/target/ \
		$(PER_PACKAGE_DIR)/$(pkg)/target/ \
		$(TARGET_DIR)$(sep))
	@$(call MESSAGE,"Creating global host directory")
	$(foreach pkg,$(PACKAGES),\
		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/host/ \
		$(PER_PACKAGE_DIR)/$(pkg)/host/ \
		$(HOST_DIR)$(sep))

But I guess that's OK. If we find that too weird, I can always
introduce some some make helpers:

per-package-target-dir = $(PER_PACKAGE_DIR)/$(1)/target
per-package-host-dir = $(PER_PACKAGE_DIR)/$(1)/host

But:

  $(call per-package-target-dir,$(pkg))

is not really shorter than

  $(PER_PACKAGE_DIR)/$(1)/target

It is actually longer :-)

Thomas
Thomas Petazzoni Nov. 24, 2017, 2:49 p.m. UTC | #5
Hello,

On Wed, 8 Nov 2017 00:57:40 +0100, Arnout Vandecappelle wrote:

> > To me, the creation
> > of the final/common HOST_DIR/TARGET_DIR/STAGING_DIR is not at
> > all something that needs to be done while the build is on-going. And I
> > could go even further: unless you call "make sdk", we technically don't
> > really need to create the final/common HOST_DIR/STAGING_DIR.  
> 
>  Absolutely true, it would make sense to do that in 'make sdk'.
> 
> > Only a
> > TARGET_DIR is needed, and perhaps even we could avoid a global one, and
> > instead have a per-filesystem image one, in order to avoid the parallel
> > filesystem image creation problem.  
> 
>  That's a great idea!

For those two things, I believe there is one reason to keep creating
the global HOST_DIR and TARGET_DIR in target-finalize: backward
compatibility with user habits. It is going to be really weird for a
lot of our users if output/host and output/target are empty at the end
of the build.

There is already something really weird going on with my changes: if
you run "make foobar" but BR2_PACKAGE_FOOBAR is not enabled, then
foobar is built, installed in its per-package directory, but because it
is not enabled at the Kconfig level, this package is not in PACKAGES,
so it is not copied to the global TARGET_DIR. So you don't have foobar
installed in output/target, nor in your final root filesystem image!

> >>> diff --git a/package/pkg-generic.mk b/package/pkg-generic.mk
> >>> index 82f8c06821..aee4011f63 100644
> >>> --- a/package/pkg-generic.mk
> >>> +++ b/package/pkg-generic.mk
> >>> @@ -158,6 +158,7 @@ $(BUILD_DIR)/%/.stamp_actual_downloaded:
> >>>  $(BUILD_DIR)/%/.stamp_extracted:
> >>>  	@$(call step_start,extract)
> >>>  	@$(call MESSAGE,"Extracting")
> >>> +	@mkdir -p $(HOST_DIR) $(TARGET_DIR) $(STAGING_DIR)    
> >>
> >>  Why do you create them here? It would make more sense to do it just before the
> >> rsync, no?  
> > 
> > I don't remember, I'll have to check again. I don't immediately see a
> > reason why they would be needed before the rsync.  

Following my proposal in my previous e-mail to have
EXTRACT_DEPENDENCIES rsync'ed into the per-package host/target at
extract time, it makes sense to create those folders at the extract
step :)

> 
>  A package without any dependencies will not do the rsync, and I do think you
> want the directories to exist. Or maybe not, I'm not sure... Actually, shouldn't
> a skeleton be installed there as well?

For the target directory, there is no problem: all target packages
depend on the skeleton package, so there is always a skeleton in the
per-package target directory.

> >>  Given the possible host-tar and host-lzip dependencies, the rsync should be
> >> done here already, not in the configure step. Ouch, that's not good, because the
> >> dependencies are only evaluated for the configure step... There are
> >> PATCH_DEPENDENCIES, but those are only before patch. Well, I guess that's a
> >> reason to keep host-tar and host-lzip as DEPENDENCIES_HOST_PREREQ :-)  
> > 
> > These are all topics that I haven't looked at yet. So thanks for
> > pointing them out. I should probably collect a TODO list on per-package
> > SDK to not forget about all the things people have mentioned as things
> > to look at.  
> 
>  Yeah, a skeleton HOST_DIR that every package depends on would simplify that,
> perhaps.

The skeleton HOST_DIR is just:

$(HOST_DIR)/usr: $(HOST_DIR)
        @ln -snf . $@

$(HOST_DIR)/lib: $(HOST_DIR)
        @mkdir -p $@
        @case $(HOSTARCH) in \
                (*64) ln -snf lib $(@D)/lib64;; \
                (*)   ln -snf lib $(@D)/lib32;; \
        esac

But yes, perhaps that could be clarified as a host-skeleton package ?

I'll have to double check how this works with my current proposed
implementation.

Best regards,

Thomas
Arnout Vandecappelle Nov. 25, 2017, 5:30 p.m. UTC | #6
On 24-11-17 15:43, Thomas Petazzoni wrote:
> Hello,
> 
> A few more answers/topics I wanted to discuss.
> 
> On Tue, 7 Nov 2017 23:50:52 +0100, Arnout Vandecappelle wrote:
> 
>>  Given the possible host-tar and host-lzip dependencies, the rsync
>> should be done here already, not in the configure step. Ouch, that's
>> not good, because the dependencies are only evaluated for the
>> configure step... There are PATCH_DEPENDENCIES, but those are only
>> before patch. Well, I guess that's a reason to keep host-tar and
>> host-lzip as DEPENDENCIES_HOST_PREREQ :-)
> 
> Here is how I'm thinking of solving the problem:
> 
>  - Next to <pkg>_PATCH_DEPENDENCIES, introduce the concept of
>    <pkg>_EXTRACT_DEPENDENCIES.

 _PATCH_DEPENDENCIES is different: it introduces a dependency between foo-patch
and bar-patch. Here you want a dependency between foo-extract and tar.

> 
>  - If there is no suitable tar in the system, then host-tar would be
>    added to <pkg>_EXTRACT_DEPENDENCIES of all packages, except host-tar
>    itself.

 So you need an additional NO_EXTRACT_DEPENDENCIES or something to exclude it
for tar...


>  - If the package needs lzip and there is no lzip available on the
>    system, host-lzip is added to <pkg>_EXTRACT_DEPENDENCIES.
> 
>  - If ccache support is enabled, host-ccache is added to
>    <pkg>_DEPENDENCIES of all packages, except host-tar, which is built
>    with HOSTCC_NOCCACHE.
> 
>  - At the beginning of the extract step of a package, we rsync to its
>    per-package target/host directories the per-package target/host dirs
>    of the packages listed in its <pkg>_EXTRACT_DEPENDENCIES variable.
> 
>  - At the beginning of the patch step of a package, we rsync to its
>    per-package target/host directories the per-package target/host dirs
>    of the packages listed in its <pkg>_PATCH_DEPENDENCIES variable.

 So this bit doesn't make sense: _PATCH_DEPENDENCIES are not (necessarily) built
before foo-patch. So to be reproducible, it should *not* be rsynced yet.

> 
> Thoughts?

 I'm not at all happy with this approach. It adds generic-package stuff that is
used for only one package, and it spreads the logic out over different places.

 I'd rather make the logic explicit in dependencies.mk. Something like

ifneq ($(filter host-tar,$(DEPENDENCIES_HOST_PREREQ)),)
$(filter-out host-tar,$(DEPENDENCIES_HOST_PREREQ)): host-tar
endif

ifneq ($(filter host-ccache,$(DEPENDENCIES_HOST_PREREQ)),)
$(filter-out host-tar host-ccache,$(DEPENDENCIES_HOST_PREREQ)): host-ccache
endif

etc. You'll probably want to introduce a variable to collect the filter-out
things, though.


 Regards,
 Arnout


> 
>>> +$(2)_HOST_DIR		= $$(PER_PACKAGE_DIR)/$(1)/host/
>>> +$(2)_STAGING_DIR	= $$(PER_PACKAGE_DIR)/$(1)/host/$(STAGING_SUBDIR)  
>>
>>  I hate adding more per-package variables. Can't you use the expanded values,
>> substituting $($(PKG)_NAME) for $(1)? Or maybe even better, change the
>> definition of TARGET_DIR etc:
>>
>> TARGET_DIR = $(if
>> $(PKG),$(PER_PACKAGE_DIR)/$($(PKG)_RAWNAME)/target,$(BASE_DIR)/target)
>>
>>  That way you also don't need to pass those variables to each individual stamp rule.
> 
> I've changed this in my new iteration. The only drawback is that since
> we no longer have those shortcut variables, the foreach loops building
> the per-package directories, and building the final global directories
> are a bit less readable:
> 
> Per-package host/target creation:
> 
> 	$(foreach pkg,$($(PKG)_FINAL_DEPENDENCIES),\
> 		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/host/ \
> 		$(PER_PACKAGE_DIR)/$(pkg)/host/ \
> 		$(HOST_DIR)$(sep))
> 	$(foreach pkg,$($(PKG)_FINAL_DEPENDENCIES),\
> 		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/target/ \
> 		$(PER_PACKAGE_DIR)/$(pkg)/target/ \
> 		$(TARGET_DIR)$(sep))
> 
> Global host/target creation in target-finalize:
> 
> 	$(foreach pkg,$(PACKAGES),\
> 		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/target/ \
> 		$(PER_PACKAGE_DIR)/$(pkg)/target/ \
> 		$(TARGET_DIR)$(sep))
> 	@$(call MESSAGE,"Creating global host directory")
> 	$(foreach pkg,$(PACKAGES),\
> 		rsync -a --link-dest=$(PER_PACKAGE_DIR)/$(pkg)/host/ \
> 		$(PER_PACKAGE_DIR)/$(pkg)/host/ \
> 		$(HOST_DIR)$(sep))
> 
> But I guess that's OK. If we find that too weird, I can always
> introduce some some make helpers:
> 
> per-package-target-dir = $(PER_PACKAGE_DIR)/$(1)/target
> per-package-host-dir = $(PER_PACKAGE_DIR)/$(1)/host
> 
> But:
> 
>   $(call per-package-target-dir,$(pkg))
> 
> is not really shorter than
> 
>   $(PER_PACKAGE_DIR)/$(1)/target
> 
> It is actually longer :-)
> 
> Thomas
>
Thomas Petazzoni Nov. 25, 2017, 8:01 p.m. UTC | #7
Hello,

On Sat, 25 Nov 2017 18:30:26 +0100, Arnout Vandecappelle wrote:

> > Here is how I'm thinking of solving the problem:
> > 
> >  - Next to <pkg>_PATCH_DEPENDENCIES, introduce the concept of
> >    <pkg>_EXTRACT_DEPENDENCIES.  
> 
>  _PATCH_DEPENDENCIES is different: it introduces a dependency between foo-patch
> and bar-patch. Here you want a dependency between foo-extract and tar.

True.

>  So this bit doesn't make sense: _PATCH_DEPENDENCIES are not (necessarily) built
> before foo-patch. So to be reproducible, it should *not* be rsynced yet.
> 
> > 
> > Thoughts?  
> 
>  I'm not at all happy with this approach. It adds generic-package stuff that is
> used for only one package, and it spreads the logic out over different places.
> 
>  I'd rather make the logic explicit in dependencies.mk. Something like
> 
> ifneq ($(filter host-tar,$(DEPENDENCIES_HOST_PREREQ)),)
> $(filter-out host-tar,$(DEPENDENCIES_HOST_PREREQ)): host-tar
> endif
> 
> ifneq ($(filter host-ccache,$(DEPENDENCIES_HOST_PREREQ)),)
> $(filter-out host-tar host-ccache,$(DEPENDENCIES_HOST_PREREQ)): host-ccache
> endif

I don't understand how this can work.

The big thing to remember is that when I'm building a package, it only
sees in its per-package host/staging directory the dependencies
explicitly listed in this package <pkg>_DEPENDENCIES variable.

With your approach, neither host-tar nor host-ccache are listed in any
package <pkg>_DEPENDENCIES variable, so the per-package host directory
of host-ccache and host-tar will never be rsync'ed into the per-package
host directories of other packages.

Due to this, the package infrastructure _will_ have to know about the
fact that all packages depend on host-tar/host-ccache, for the simple
reason that we need to know that we have to rsync host-tar/host-ccache
when populating the per-package host directory in the configure step.

Best regards,

Thomas
Yann E. MORIN Nov. 27, 2017, 2:23 p.m. UTC | #8
Thomas, Arnout, All,

On 2017-11-25 21:01 +0100, Thomas Petazzoni spake thusly:
> On Sat, 25 Nov 2017 18:30:26 +0100, Arnout Vandecappelle wrote:
> 
> > > Here is how I'm thinking of solving the problem:
> > > 
> > >  - Next to <pkg>_PATCH_DEPENDENCIES, introduce the concept of
> > >    <pkg>_EXTRACT_DEPENDENCIES.  
> > 
> >  _PATCH_DEPENDENCIES is different: it introduces a dependency between foo-patch
> > and bar-patch. Here you want a dependency between foo-extract and tar.

And I think this is a sane approach. We already have such requirements
for tar, lzip and xz.

So we already have three cases where we might want to add an extract
dependency, which is imho sufficient to justify support in the infra.

Besides, having the dependencies managed from the infra will guarantee
the build ordering and the fact that they are built only when required.

> >  So this bit doesn't make sense: _PATCH_DEPENDENCIES are not (necessarily) built
> > before foo-patch. So to be reproducible, it should *not* be rsynced yet.
> > 
> > > 
> > > Thoughts?  
> > 
> >  I'm not at all happy with this approach. It adds generic-package stuff that is
> > used for only one package, and it spreads the logic out over different places.

Quite the opposite: it is used for at least three packages, and it
gathers all the dependencies under the aegis of the package infra.

> >  I'd rather make the logic explicit in dependencies.mk. Something like
> > 
> > ifneq ($(filter host-tar,$(DEPENDENCIES_HOST_PREREQ)),)
> > $(filter-out host-tar,$(DEPENDENCIES_HOST_PREREQ)): host-tar
> > endif
> > 
> > ifneq ($(filter host-ccache,$(DEPENDENCIES_HOST_PREREQ)),)
> > $(filter-out host-tar host-ccache,$(DEPENDENCIES_HOST_PREREQ)): host-ccache
> > endif
> 
> I don't understand how this can work.

TBH, I have a bit of an issue following it as well... :-/

> The big thing to remember is that when I'm building a package, it only
> sees in its per-package host/staging directory the dependencies
> explicitly listed in this package <pkg>_DEPENDENCIES variable.
> 
> With your approach, neither host-tar nor host-ccache are listed in any
> package <pkg>_DEPENDENCIES variable, so the per-package host directory
> of host-ccache and host-tar will never be rsync'ed into the per-package
> host directories of other packages.
> 
> Due to this, the package infrastructure _will_ have to know about the
> fact that all packages depend on host-tar/host-ccache, for the simple
> reason that we need to know that we have to rsync host-tar/host-ccache
> when populating the per-package host directory in the configure step.

I think we should strive at moving as much as possible to packages and
the package infra.

Regards,
Yann E. MORIN.

> 
> Best regards,
> 
> Thomas
> -- 
> Thomas Petazzoni, CTO, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 5496273329..4e1ba62569 100644
--- a/Makefile
+++ b/Makefile
@@ -216,6 +216,8 @@  BR_GRAPH_OUT := $(or $(BR2_GRAPH_OUT),pdf)
 BUILD_DIR := $(BASE_DIR)/build
 BINARIES_DIR := $(BASE_DIR)/images
 TARGET_DIR := $(BASE_DIR)/target
+PER_PACKAGE_DIR := $(BASE_DIR)/per-package
+
 # initial definition so that 'make clean' works for most users, even without
 # .config. HOST_DIR will be overwritten later when .config is included.
 HOST_DIR := $(BASE_DIR)/host
@@ -231,7 +233,7 @@  LEGAL_MANIFEST_CSV_HOST = $(LEGAL_INFO_DIR)/host-manifest.csv
 LEGAL_WARNINGS = $(LEGAL_INFO_DIR)/.warnings
 LEGAL_REPORT = $(LEGAL_INFO_DIR)/README
 
-export LD_LIBRARY_PATH = $(if $(PKG),$(HOST_DIR)/lib)
+export LD_LIBRARY_PATH = $(if $(PKG),$($(PKG)_HOST_DIR)/lib)
 
 ################################################################################
 #
@@ -239,7 +241,7 @@  export LD_LIBRARY_PATH = $(if $(PKG),$(HOST_DIR)/lib)
 # dependencies anywhere else
 #
 ################################################################################
-$(BUILD_DIR) $(TARGET_DIR) $(HOST_DIR) $(BINARIES_DIR) $(LEGAL_INFO_DIR) $(REDIST_SOURCES_DIR_TARGET) $(REDIST_SOURCES_DIR_HOST):
+$(BUILD_DIR) $(TARGET_DIR) $(HOST_DIR) $(BINARIES_DIR) $(LEGAL_INFO_DIR) $(REDIST_SOURCES_DIR_TARGET) $(REDIST_SOURCES_DIR_HOST) $(PER_PACKAGE_DIR):
 	@mkdir -p $@
 
 BR2_CONFIG = $(CONFIG_DIR)/.config
@@ -675,10 +677,19 @@  endef
 TARGET_FINALIZE_HOOKS += PURGE_LOCALES
 endif
 
+ALL_PACKAGES_TARGET_DIRS = $(foreach pkg,$(PACKAGES),$($(call UPPERCASE,$(pkg))_TARGET_DIR))
+ALL_PACKAGES_HOST_DIRS   = $(foreach pkg,$(PACKAGES),$($(call UPPERCASE,$(pkg))_HOST_DIR))
+
 $(TARGETS_ROOTFS): target-finalize
 
 .PHONY: target-finalize
 target-finalize: $(PACKAGES)
+	@$(call MESSAGE,"Creating global target directory")
+	$(foreach dir,$(ALL_PACKAGES_TARGET_DIRS),\
+		rsync -au --link-dest=$(dir) $(dir) $(TARGET_DIR)$(sep))
+	@$(call MESSAGE,"Creating global host directory")
+	$(foreach dir,$(ALL_PACKAGES_HOST_DIRS),\
+		rsync -au --link-dest=$(dir) $(dir) $(HOST_DIR)$(sep))
 	@$(call MESSAGE,"Finalizing target directory")
 	$(TOPDIR)/support/scripts/fix-rpath host
 	$(foreach hook,$(TARGET_FINALIZE_HOOKS),$($(hook))$(sep))
@@ -972,7 +983,7 @@  printvars:
 clean:
 	rm -rf $(TARGET_DIR) $(BINARIES_DIR) $(HOST_DIR) \
 		$(BUILD_DIR) $(BASE_DIR)/staging \
-		$(LEGAL_INFO_DIR) $(GRAPHS_DIR)
+		$(LEGAL_INFO_DIR) $(GRAPHS_DIR) $(PER_PACKAGE_DIR)
 
 .PHONY: distclean
 distclean: clean
diff --git a/package/pkg-generic.mk b/package/pkg-generic.mk
index 82f8c06821..aee4011f63 100644
--- a/package/pkg-generic.mk
+++ b/package/pkg-generic.mk
@@ -158,6 +158,7 @@  $(BUILD_DIR)/%/.stamp_actual_downloaded:
 $(BUILD_DIR)/%/.stamp_extracted:
 	@$(call step_start,extract)
 	@$(call MESSAGE,"Extracting")
+	@mkdir -p $(HOST_DIR) $(TARGET_DIR) $(STAGING_DIR)
 	$(foreach hook,$($(PKG)_PRE_EXTRACT_HOOKS),$(call $(hook))$(sep))
 	$(Q)mkdir -p $(@D)
 	$($(PKG)_EXTRACT_CMDS)
@@ -215,6 +216,10 @@  $(foreach dir,$(call qstrip,$(BR2_GLOBAL_PATCH_DIR)),\
 $(BUILD_DIR)/%/.stamp_configured:
 	@$(call step_start,configure)
 	@$(call MESSAGE,"Configuring")
+	$(foreach dir,$($(PKG)_FINAL_DEPENDENCIES_HOST_DIRS),\
+		rsync -au --link-dest=$(dir) $(dir) $(HOST_DIR)$(sep))
+	$(foreach dir,$($(PKG)_FINAL_DEPENDENCIES_TARGET_DIRS),\
+		rsync -au --link-dest=$(dir) $(dir) $(TARGET_DIR)$(sep))
 	$(foreach hook,$($(PKG)_PRE_CONFIGURE_HOOKS),$(call $(hook))$(sep))
 	$($(PKG)_CONFIGURE_CMDS)
 	$(foreach hook,$($(PKG)_POST_CONFIGURE_HOOKS),$(call $(hook))$(sep))
@@ -409,6 +414,10 @@  $(2)_NAME			=  $(1)
 $(2)_RAWNAME			=  $$(patsubst host-%,%,$(1))
 $(2)_PKGDIR			=  $(pkgdir)
 
+$(2)_TARGET_DIR		= $$(PER_PACKAGE_DIR)/$(1)/target/
+$(2)_HOST_DIR		= $$(PER_PACKAGE_DIR)/$(1)/host/
+$(2)_STAGING_DIR	= $$(PER_PACKAGE_DIR)/$(1)/host/$(STAGING_SUBDIR)
+
 # Keep the package version that may contain forward slashes in the _DL_VERSION
 # variable, then replace all forward slashes ('/') by underscores ('_') to
 # sanitize the package version that is used in paths, directory and file names.
@@ -561,6 +570,13 @@  $(2)_FINAL_DEPENDENCIES = $$(sort $$($(2)_DEPENDENCIES))
 $(2)_FINAL_PATCH_DEPENDENCIES = $$(sort $$($(2)_PATCH_DEPENDENCIES))
 $(2)_FINAL_ALL_DEPENDENCIES = $$(sort $$($(2)_FINAL_DEPENDENCIES) $$($(2)_FINAL_PATCH_DEPENDENCIES))
 
+$(2)_FINAL_DEPENDENCIES_HOST_DIRS = \
+	$$(foreach dep,$$($(2)_FINAL_DEPENDENCIES),\
+		$$($$(call UPPERCASE,$$(dep))_HOST_DIR))
+$(2)_FINAL_DEPENDENCIES_TARGET_DIRS = \
+	$$(foreach dep,$$($(2)_FINAL_DEPENDENCIES),\
+		$$($$(call UPPERCASE,$$(dep))_TARGET_DIR))
+
 $(2)_INSTALL_STAGING		?= NO
 $(2)_INSTALL_IMAGES		?= NO
 $(2)_INSTALL_TARGET		?= YES
@@ -789,17 +805,38 @@  $(1)-reconfigure:	$(1)-clean-for-reconfigure $(1)
 # define the PKG variable for all targets, containing the
 # uppercase package variable prefix
 $$($(2)_TARGET_INSTALL_TARGET):		PKG=$(2)
+$$($(2)_TARGET_INSTALL_TARGET):		HOST_DIR=$$($(2)_HOST_DIR)
+$$($(2)_TARGET_INSTALL_TARGET):		STAGING_DIR=$$($(2)_STAGING_DIR)
+$$($(2)_TARGET_INSTALL_TARGET):		TARGET_DIR=$$($(2)_TARGET_DIR)
 $$($(2)_TARGET_INSTALL_STAGING):	PKG=$(2)
+$$($(2)_TARGET_INSTALL_STAGING):	HOST_DIR=$$($(2)_HOST_DIR)
+$$($(2)_TARGET_INSTALL_STAGING):	STAGING_DIR=$$($(2)_STAGING_DIR)
+$$($(2)_TARGET_INSTALL_STAGING):	TARGET_DIR=$$($(2)_TARGET_DIR)
 $$($(2)_TARGET_INSTALL_IMAGES):		PKG=$(2)
+$$($(2)_TARGET_INSTALL_IMAGES):		HOST_DIR=$$($(2)_HOST_DIR)
+$$($(2)_TARGET_INSTALL_IMAGES):		STAGING_DIR=$$($(2)_STAGING_DIR)
+$$($(2)_TARGET_INSTALL_IMAGES):		TARGET_DIR=$$($(2)_TARGET_DIR)
 $$($(2)_TARGET_INSTALL_HOST):		PKG=$(2)
+$$($(2)_TARGET_INSTALL_HOST):		HOST_DIR=$$($(2)_HOST_DIR)
+$$($(2)_TARGET_INSTALL_HOST):		STAGING_DIR=$$($(2)_STAGING_DIR)
+$$($(2)_TARGET_INSTALL_HOST):		TARGET_DIR=$$($(2)_TARGET_DIR)
 $$($(2)_TARGET_BUILD):			PKG=$(2)
+$$($(2)_TARGET_BUILD):			HOST_DIR=$$($(2)_HOST_DIR)
+$$($(2)_TARGET_BUILD):			STAGING_DIR=$$($(2)_STAGING_DIR)
+$$($(2)_TARGET_BUILD):			TARGET_DIR=$$($(2)_TARGET_DIR)
 $$($(2)_TARGET_CONFIGURE):		PKG=$(2)
+$$($(2)_TARGET_CONFIGURE):		HOST_DIR=$$($(2)_HOST_DIR)
+$$($(2)_TARGET_CONFIGURE):		STAGING_DIR=$$($(2)_STAGING_DIR)
+$$($(2)_TARGET_CONFIGURE):		TARGET_DIR=$$($(2)_TARGET_DIR)
 $$($(2)_TARGET_RSYNC):			SRCDIR=$$($(2)_OVERRIDE_SRCDIR)
 $$($(2)_TARGET_RSYNC):			PKG=$(2)
 $$($(2)_TARGET_PATCH):			PKG=$(2)
 $$($(2)_TARGET_PATCH):			RAWNAME=$$(patsubst host-%,%,$(1))
 $$($(2)_TARGET_PATCH):			PKGDIR=$(pkgdir)
 $$($(2)_TARGET_EXTRACT):		PKG=$(2)
+$$($(2)_TARGET_EXTRACT):		HOST_DIR=$$($(2)_HOST_DIR)
+$$($(2)_TARGET_EXTRACT):		STAGING_DIR=$$($(2)_STAGING_DIR)
+$$($(2)_TARGET_EXTRACT):		TARGET_DIR=$$($(2)_TARGET_DIR)
 $$($(2)_TARGET_SOURCE):			PKG=$(2)
 $$($(2)_TARGET_SOURCE):			PKGDIR=$(pkgdir)
 $$($(2)_TARGET_ACTUAL_SOURCE):		PKG=$(2)