diff mbox series

[05/10] base: Switch UNPACKDIR to a subdir of WORKDIR

Message ID 20240515115620.420558-5-richard.purdie@linuxfoundation.org
State Accepted, archived
Commit b84eec5c4cbf4b39d6712800dd0d2fe5337721cb
Headers show
Series [01/10] recipes: Start WORKDIR -> UNPACKDIR transition | expand

Commit Message

Richard Purdie May 15, 2024, 11:56 a.m. UTC
Change do_unpack to unpack files to a subdirectory of WORKDIR instead of WORKDIR
itself. There are several good reasons for this but it is mainly about being able
to isolate the output of the unpack task and tell the files apart from other things
which are created in workdir (logs, sysroots, temp dirs and more).

This means that when the do_unpack task reruns, we can clean UNPACKDIR and know
we have a standard point to start builds from.

It also makes code in tools like devtool and recipetool easier.

To reduce the impact to users, if a subdirectory under UNPACKDIR matches
the first subdirectory under WORKDIR of S, that directory is moved into position
inside WORKDIR. This preserves the behaviour of S = "${WORKDIR}/git",
S = "${WORKDIR}/${BPN}" and other commonly used source directory setups.

The directory is moved since sadly many autotools based projects can't cope with
symlinks in their paths.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/classes-global/base.bbclass | 21 ++++++++++++++++++---
 meta/conf/bitbake.conf           |  2 +-
 2 files changed, 19 insertions(+), 4 deletions(-)

Comments

Peter Kjellerstedt May 15, 2024, 5:42 p.m. UTC | #1
> -----Original Message-----
> From: openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org> On Behalf Of Richard Purdie
> Sent: den 15 maj 2024 13:56
> To: openembedded-core@lists.openembedded.org
> Subject: [OE-core] [PATCH 05/10] base: Switch UNPACKDIR to a subdir of WORKDIR
> 
> Change do_unpack to unpack files to a subdirectory of WORKDIR instead of WORKDIR
> itself. There are several good reasons for this but it is mainly about being able
> to isolate the output of the unpack task and tell the files apart from other things
> which are created in workdir (logs, sysroots, temp dirs and more).
> 
> This means that when the do_unpack task reruns, we can clean UNPACKDIR and know
> we have a standard point to start builds from.
> 
> It also makes code in tools like devtool and recipetool easier.
> 
> To reduce the impact to users, if a subdirectory under UNPACKDIR matches
> the first subdirectory under WORKDIR of S, that directory is moved into position
> inside WORKDIR. This preserves the behaviour of S = "${WORKDIR}/git",
> S = "${WORKDIR}/${BPN}" and other commonly used source directory setups.
> 
> The directory is moved since sadly many autotools based projects can't cope with
> symlinks in their paths.

Would it be an option to create a symbolic link by default, and instead 
let the autotools bbclass replace it with a moved directory? If it is 
in fact only autotools based projects that have this problem.

> 
> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> ---
>  meta/classes-global/base.bbclass | 21 ++++++++++++++++++---
>  meta/conf/bitbake.conf           |  2 +-
>  2 files changed, 19 insertions(+), 4 deletions(-)
> 
> diff --git a/meta/classes-global/base.bbclass b/meta/classes-
> global/base.bbclass
> index 066f3848f7c..cdce0538273 100644
> --- a/meta/classes-global/base.bbclass
> +++ b/meta/classes-global/base.bbclass
> @@ -153,20 +153,35 @@ python base_do_fetch() {
>  }
> 
>  addtask unpack after do_fetch
> -do_unpack[dirs] = "${UNPACKDIR}"
> -
> -do_unpack[cleandirs] = "${@d.getVar('S') if os.path.normpath(d.getVar('S')) != os.path.normpath(d.getVar('WORKDIR')) else os.path.join('${S}', 'patches')}"
> +do_unpack[cleandirs] = "${UNPACKDIR}"
> 
>  python base_do_unpack() {
> +    import shutil
> +
>      src_uri = (d.getVar('SRC_URI') or "").split()
>      if not src_uri:
>          return
> 
> +    sourcedir = d.getVar('S')
> +    basedir = None
> +    workdir = d.getVar('WORKDIR')
> +    unpackdir = d.getVar('UNPACKDIR')
> +    if sourcedir.startswith(workdir) and not sourcedir.startswith(unpackdir):
> +        basedir = sourcedir.replace(workdir, '').strip("/").split('/')[0]
> +        bb.utils.remove(sourcedir, True)

This remove() seems wrong and should not be needed. There are two 
cases here:

1) either ${S} == ${WORKDIR}, in which case the above will remove 
   ${WORKDIR}, which is sure to lead to problems, or
2) ${S} == ${WORKDIR}/foo[/...], in which case the removal of 
   workdir + '/' + basedir below will also remove ${S} as 
   basedir == "foo".

> +        if basedir:
> +            bb.utils.remove(workdir + '/' + basedir, True)
> +
>      try:
>          fetcher = bb.fetch2.Fetch(src_uri, d)
>          fetcher.unpack(d.getVar('UNPACKDIR'))
>      except bb.fetch2.BBFetchException as e:
>          bb.fatal("Bitbake Fetcher Error: " + repr(e))
> +
> +    if basedir and os.path.exists(unpackdir + '/' + basedir):
> +        # Compatibility magic to ensure ${WORKDIR}/git and ${WORKDIR}/${BP}
> +        # as often used in S work as expected.
> +        shutil.move(unpackdir + '/' + basedir, workdir + '/' + basedir)
>  }
> 
>  SSTATETASKS += "do_deploy_source_date_epoch"
> diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
> index b2c500d8739..75c850760f6 100644
> --- a/meta/conf/bitbake.conf
> +++ b/meta/conf/bitbake.conf
> @@ -405,7 +405,7 @@ STAMP =
> "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
>  STAMPCLEAN = "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/*-*"
>  BASE_WORKDIR ?= "${TMPDIR}/work"
>  WORKDIR = "${BASE_WORKDIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
> -UNPACKDIR ??= "${WORKDIR}"
> +UNPACKDIR ??= "${WORKDIR}/sources-unpack"

"sources-unpack" does not exactly fall off the tongue.
Would "unpack" or "unpacked" be alternatives?

>  T = "${WORKDIR}/temp"
>  D = "${WORKDIR}/image"
>  S = "${WORKDIR}/${BP}"
> --
> 2.40.1

//Peter
Richard Purdie May 15, 2024, 6:28 p.m. UTC | #2
On Wed, 2024-05-15 at 17:42 +0000, Peter Kjellerstedt wrote:
> > -----Original Message-----
> > From: openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org> On Behalf Of Richard Purdie
> > Sent: den 15 maj 2024 13:56
> > To: openembedded-core@lists.openembedded.org
> > Subject: [OE-core] [PATCH 05/10] base: Switch UNPACKDIR to a subdir of WORKDIR
> > 
> > Change do_unpack to unpack files to a subdirectory of WORKDIR instead of WORKDIR
> > itself. There are several good reasons for this but it is mainly about being able
> > to isolate the output of the unpack task and tell the files apart from other things
> > which are created in workdir (logs, sysroots, temp dirs and more).
> > 
> > This means that when the do_unpack task reruns, we can clean UNPACKDIR and know
> > we have a standard point to start builds from.
> > 
> > It also makes code in tools like devtool and recipetool easier.
> > 
> > To reduce the impact to users, if a subdirectory under UNPACKDIR matches
> > the first subdirectory under WORKDIR of S, that directory is moved into position
> > inside WORKDIR. This preserves the behaviour of S = "${WORKDIR}/git",
> > S = "${WORKDIR}/${BPN}" and other commonly used source directory setups.
> > 
> > The directory is moved since sadly many autotools based projects can't cope with
> > symlinks in their paths.
> 
> Would it be an option to create a symbolic link by default, and instead 
> let the autotools bbclass replace it with a moved directory? If it is
> in fact only autotools based projects that have this problem.

Maybe. I've reached the point with this where I really didn't want to
try and explore that particular set of problems and I'd rather things
were consistent.

I'm not going to rule out trying to clean up and improve this somehow
in the future but for now, it works, it solves the original major
problem (cleanup of obsolete sources) and we have most of the major
pieces like devtool working. It also doesn't look as different to users
as moving S would from a documentation perspective.

I'm a bit torn on what the end result for S should look like. I'm happy
enough with the changes so far and I think the best thing to do is
merge these, then consider if we do want to go further or not.

> > Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> > ---
> >  meta/classes-global/base.bbclass | 21 ++++++++++++++++++---
> >  meta/conf/bitbake.conf           |  2 +-
> >  2 files changed, 19 insertions(+), 4 deletions(-)
> > 
> > diff --git a/meta/classes-global/base.bbclass b/meta/classes-
> > global/base.bbclass
> > index 066f3848f7c..cdce0538273 100644
> > --- a/meta/classes-global/base.bbclass
> > +++ b/meta/classes-global/base.bbclass
> > @@ -153,20 +153,35 @@ python base_do_fetch() {
> >  }
> > 
> >  addtask unpack after do_fetch
> > -do_unpack[dirs] = "${UNPACKDIR}"
> > -
> > -do_unpack[cleandirs] = "${@d.getVar('S') if os.path.normpath(d.getVar('S')) != os.path.normpath(d.getVar('WORKDIR')) else os.path.join('${S}', 'patches')}"
> > +do_unpack[cleandirs] = "${UNPACKDIR}"
> > 
> >  python base_do_unpack() {
> > +    import shutil
> > +
> >      src_uri = (d.getVar('SRC_URI') or "").split()
> >      if not src_uri:
> >          return
> > 
> > +    sourcedir = d.getVar('S')
> > +    basedir = None
> > +    workdir = d.getVar('WORKDIR')
> > +    unpackdir = d.getVar('UNPACKDIR')
> > +    if sourcedir.startswith(workdir) and not sourcedir.startswith(unpackdir):
> > +        basedir = sourcedir.replace(workdir, '').strip("/").split('/')[0]
> > +        bb.utils.remove(sourcedir, True)
> 
> This remove() seems wrong and should not be needed. There are two 
> cases here:
> 
> 1) either ${S} == ${WORKDIR}, in which case the above will remove 
>    ${WORKDIR}, which is sure to lead to problems, or

Which is why S = WORKDIR has to become a hard error. We have to drop
support for it.

> 2) ${S} == ${WORKDIR}/foo[/...], in which case the removal of 
>    workdir + '/' + basedir below will also remove ${S} as 
>    basedir == "foo".

You're right. This code did go through several iterations as I found
all the weird corner cases this needs to support. Ultimately, I suspect
it should probably unconditionally remove ${S}. I'd need yet another
test run to try and identify if that causes any new weird issues
though.

> 
> > +        if basedir:
> > +            bb.utils.remove(workdir + '/' + basedir, True)
> > +
> >      try:
> >          fetcher = bb.fetch2.Fetch(src_uri, d)
> >          fetcher.unpack(d.getVar('UNPACKDIR'))
> >      except bb.fetch2.BBFetchException as e:
> >          bb.fatal("Bitbake Fetcher Error: " + repr(e))
> > +
> > +    if basedir and os.path.exists(unpackdir + '/' + basedir):
> > +        # Compatibility magic to ensure ${WORKDIR}/git and ${WORKDIR}/${BP}
> > +        # as often used in S work as expected.
> > +        shutil.move(unpackdir + '/' + basedir, workdir + '/' + basedir)
> >  }
> > 
> >  SSTATETASKS += "do_deploy_source_date_epoch"
> > diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
> > index b2c500d8739..75c850760f6 100644
> > --- a/meta/conf/bitbake.conf
> > +++ b/meta/conf/bitbake.conf
> > @@ -405,7 +405,7 @@ STAMP =
> > "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
> >  STAMPCLEAN = "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/*-*"
> >  BASE_WORKDIR ?= "${TMPDIR}/work"
> >  WORKDIR = "${BASE_WORKDIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
> > -UNPACKDIR ??= "${WORKDIR}"
> > +UNPACKDIR ??= "${WORKDIR}/sources-unpack"
> 
> "sources-unpack" does not exactly fall off the tongue.
> Would "unpack" or "unpacked" be alternatives?

I don't feel they're as clear...

Cheers,

Richard
Martin Hundebøll May 16, 2024, 7:18 a.m. UTC | #3
On Wed, 2024-05-15 at 12:56 +0100, Richard Purdie wrote:
> diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
> index b2c500d8739..75c850760f6 100644
> --- a/meta/conf/bitbake.conf
> +++ b/meta/conf/bitbake.conf
> @@ -405,7 +405,7 @@ STAMP =
> "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
>  STAMPCLEAN = "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/*-*"
>  BASE_WORKDIR ?= "${TMPDIR}/work"
>  WORKDIR = "${BASE_WORKDIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
> -UNPACKDIR ??= "${WORKDIR}"
> +UNPACKDIR ??= "${WORKDIR}/sources-unpack"
>  T = "${WORKDIR}/temp"
>  D = "${WORKDIR}/image"
>  S = "${WORKDIR}/${BP}"

Why not use

  UNPACKDIR ??= "${WORKDIR}/sources"

like it's done in the individual recipes?


And shouldn't we do 

  S = ?? "${UNPACKDIR}/${BP}"

also?

// Martin
Richard Purdie May 16, 2024, 7:54 a.m. UTC | #4
On Thu, 2024-05-16 at 09:18 +0200, Martin Hundebøll wrote:
> On Wed, 2024-05-15 at 12:56 +0100, Richard Purdie wrote:
> > diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
> > index b2c500d8739..75c850760f6 100644
> > --- a/meta/conf/bitbake.conf
> > +++ b/meta/conf/bitbake.conf
> > @@ -405,7 +405,7 @@ STAMP =
> > "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
> >  STAMPCLEAN = "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/*-*"
> >  BASE_WORKDIR ?= "${TMPDIR}/work"
> >  WORKDIR = "${BASE_WORKDIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
> > -UNPACKDIR ??= "${WORKDIR}"
> > +UNPACKDIR ??= "${WORKDIR}/sources-unpack"
> >  T = "${WORKDIR}/temp"
> >  D = "${WORKDIR}/image"
> >  S = "${WORKDIR}/${BP}"
> 
> Why not use
> 
>   UNPACKDIR ??= "${WORKDIR}/sources"
> 
> like it's done in the individual recipes?

I think it is helpful for users to be able to tell the difference
between a recipe where S is a subdirectory of this and when there is no
subdirectory. I therefore left them visually different.

> And shouldn't we do 
> 
>   S = ?? "${UNPACKDIR}/${BP}"
> 
> also?

See my other emails. I'm torn on changing this. I've been hoping we
could avoid extra directory levels, the extra pain of changing S
everywhere and there is also some benefit to keeping extra unpacked
files clearly separate from the other sources. We may end up doing it,
I don't know.

Cheers,

Richard
Richard Purdie May 16, 2024, 10:45 a.m. UTC | #5
On Wed, 2024-05-15 at 17:42 +0000, Peter Kjellerstedt wrote:
> 
> > diff --git a/meta/classes-global/base.bbclass b/meta/classes-
> > global/base.bbclass
> > index 066f3848f7c..cdce0538273 100644
> > --- a/meta/classes-global/base.bbclass
> > +++ b/meta/classes-global/base.bbclass
> > @@ -153,20 +153,35 @@ python base_do_fetch() {
> >  }
> > 
> >  addtask unpack after do_fetch
> > -do_unpack[dirs] = "${UNPACKDIR}"
> > -
> > -do_unpack[cleandirs] = "${@d.getVar('S') if
> > os.path.normpath(d.getVar('S')) !=
> > os.path.normpath(d.getVar('WORKDIR')) else os.path.join('${S}',
> > 'patches')}"
> > +do_unpack[cleandirs] = "${UNPACKDIR}"
> > 
> >  python base_do_unpack() {
> > +    import shutil
> > +
> >      src_uri = (d.getVar('SRC_URI') or "").split()
> >      if not src_uri:
> >          return
> > 
> > +    sourcedir = d.getVar('S')
> > +    basedir = None
> > +    workdir = d.getVar('WORKDIR')
> > +    unpackdir = d.getVar('UNPACKDIR')
> > +    if sourcedir.startswith(workdir) and not
> > sourcedir.startswith(unpackdir):
> > +        basedir = sourcedir.replace(workdir,
> > '').strip("/").split('/')[0]
> > +        bb.utils.remove(sourcedir, True)
> 
> This remove() seems wrong and should not be needed. There are two 
> cases here:
> 
> 1) either ${S} == ${WORKDIR}, in which case the above will remove 
>    ${WORKDIR}, which is sure to lead to problems, or
> 2) ${S} == ${WORKDIR}/foo[/...], in which case the removal of 
>    workdir + '/' + basedir below will also remove ${S} as 
>    basedir == "foo".
> 
> 

I did look into that remove() further and tried to make the removal
unconditional. The challenge is that if S = UNPACKDIR, it will rmdir
the directory and the code assumes UNPACKDIR to be created from the
cleandirs. We don't want an empty S created though for the S !=
UNPACKDIR case.

Making it conditional on sourcedir != unpackdir which was the original
condition for that code block but that does still cause QA warnings for
linux-yocto. I'm now convinced linux-yocto needs more work so I'm
tempted to disable that QA check there for now, letting us improve
base.bbclass.

Cheers,

Richard, running out of patience with hours long test cycles
diff mbox series

Patch

diff --git a/meta/classes-global/base.bbclass b/meta/classes-global/base.bbclass
index 066f3848f7c..cdce0538273 100644
--- a/meta/classes-global/base.bbclass
+++ b/meta/classes-global/base.bbclass
@@ -153,20 +153,35 @@  python base_do_fetch() {
 }
 
 addtask unpack after do_fetch
-do_unpack[dirs] = "${UNPACKDIR}"
-
-do_unpack[cleandirs] = "${@d.getVar('S') if os.path.normpath(d.getVar('S')) != os.path.normpath(d.getVar('WORKDIR')) else os.path.join('${S}', 'patches')}"
+do_unpack[cleandirs] = "${UNPACKDIR}"
 
 python base_do_unpack() {
+    import shutil
+
     src_uri = (d.getVar('SRC_URI') or "").split()
     if not src_uri:
         return
 
+    sourcedir = d.getVar('S')
+    basedir = None
+    workdir = d.getVar('WORKDIR')
+    unpackdir = d.getVar('UNPACKDIR')
+    if sourcedir.startswith(workdir) and not sourcedir.startswith(unpackdir):
+        basedir = sourcedir.replace(workdir, '').strip("/").split('/')[0]
+        bb.utils.remove(sourcedir, True)
+        if basedir:
+            bb.utils.remove(workdir + '/' + basedir, True)
+
     try:
         fetcher = bb.fetch2.Fetch(src_uri, d)
         fetcher.unpack(d.getVar('UNPACKDIR'))
     except bb.fetch2.BBFetchException as e:
         bb.fatal("Bitbake Fetcher Error: " + repr(e))
+
+    if basedir and os.path.exists(unpackdir + '/' + basedir):
+        # Compatibility magic to ensure ${WORKDIR}/git and ${WORKDIR}/${BP}
+        # as often used in S work as expected.
+        shutil.move(unpackdir + '/' + basedir, workdir + '/' + basedir)
 }
 
 SSTATETASKS += "do_deploy_source_date_epoch"
diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
index b2c500d8739..75c850760f6 100644
--- a/meta/conf/bitbake.conf
+++ b/meta/conf/bitbake.conf
@@ -405,7 +405,7 @@  STAMP = "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
 STAMPCLEAN = "${STAMPS_DIR}/${MULTIMACH_TARGET_SYS}/${PN}/*-*"
 BASE_WORKDIR ?= "${TMPDIR}/work"
 WORKDIR = "${BASE_WORKDIR}/${MULTIMACH_TARGET_SYS}/${PN}/${PV}"
-UNPACKDIR ??= "${WORKDIR}"
+UNPACKDIR ??= "${WORKDIR}/sources-unpack"
 T = "${WORKDIR}/temp"
 D = "${WORKDIR}/image"
 S = "${WORKDIR}/${BP}"