Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-workspace failure when sources outside $WORKSPACE (Bugzilla Bug 4878) #10551

Open
tianocore-issues opened this issue Dec 7, 2024 · 2 comments
Labels
priority:low Little to no impact. No urgency to fix. type:bug Something isn't working

Comments

@tianocore-issues
Copy link

This issue was created automatically with bugzilla2github

Bugzilla Bug 4878

Date: 2024-12-07T18:29:51+00:00
From: Dionna Glaze <>
To: unassigned <>
CC:

Last updated: 2024-12-09T15:03:18+00:00

@tianocore-issues
Copy link
Author

Comment 23558

Date: 2024-12-07 18:29:51 +0000
From: Dionna Glaze <>

  • Industry Specification: ---
  • Release Observed: ['EDK II Master', 'edk2-stable202302', 'edk2-stable202305']
  • Releases to Fix: EDK II Master
  • Target OS: ---
  • Bugzilla Assignee(s): unassigned <>

Context: I'm trying to isolate inputs for building EDK2 using Bazel to invoke build. Mixing the locations of inputs with the locations of outputs is not allowed. It is enforced with per-build hermetic directories.

Related https://bugzilla.tianocore.org/show_bug.cgi?id=2232

If I interpret this correctly,
https://github.com/tianocore/tianocore.github.io/wiki/Multiple_Workspace

WORKSPACE can be empty and just be where outputs will go.
Input sources should be expressible in PACKAGES_PATH.

Say I have sources in /src/, and create an empty /ws/ for my build.
WORKSPACE=$(pwd)/ws/
PACKAGES_PATH=$(pwd)/edk2_files/

I can pre-generate the Conf/ files and include them in /src/.

If I run build from $WORKSPACE and provide a dsc file path relative to $PACKAGES_PATH, then it is able to find it. Autogen produces a makefile that does not reference the appropriate inf file, however.

make: *** No rule to make target `/build/work/87135c5654f90fdaab9d9df77c696a90ec61/bazel-out/k8-fastbuild/genfiles/third_party/edk2/ws/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf&apos;, needed by `/build/work/87135c5654f90fdaab9d9df77c696a90ec61/bazel-out/k8-fastbuild/genfiles/third_party/edk2/ws/Build/OvmfX64/DEBUG_CLANGDWARF/FV/Ffs/B8E62775-BB0A-43f0-A843-5BE8B14F8CCDBootGraphicsResourceTableDxe/B8E62775-BB0A-43f0-A843-5BE8B14F8CCDSEC3.ui&apos;.  Stop.

I'm confused why the makefile rule is relative to the workspace when the inf comes from the packages_path. Is this by design to allow different PACKAGES_PATH modules to use inf paths relative to.... something else? I can see that working if you have a very specific semantics for multiple workspace composition where there's a composed source tree staging area for all combined workspaces. The paths could then be relative to the staging area. That doesn't appear to be how it operates, however.

@tianocore-issues
Copy link
Author

Comment 23559

Date: 2024-12-08 02:15:13 +0000
From: Dionna Glaze <>

I've been able to narrow down the failure to 2 things:

  1. python multithreading doing silly things with environment variables, and
  2. FdfParser's PreprocessIncludeFile not also searching PACKAGES_PATH.

With the following changes and strictly using -n 1 instead of the -n 16 I built with before, I can get the build to succeed.

In GenFds.py, I changed the os.getenv calls to instead use a backup os_environ

        # for some reason, os.getenv(&quot;WORKSPACE&quot;) or os.environ[&quot;WORKSPACE&quot;]
        # sometimes does&apos;t work while os.environb[b&quot;WORKSPACE&quot;] always works
        os_environ = {k.decode(&apos;utf8&apos;): pyos.environb[k].decode(&apos;utf8&apos;) for k in pyos.environb}
        if not FdsCommandDict.get(&quot;Workspace&quot;,os_environ.get(&apos;WORKSPACE&apos;)):

(Note, pyos is from import os as pyos in the preamble)

and later

        PackagesPath = os.getenv(&quot;PACKAGES_PATH&quot;)

becomes

        PackagesPath = os_environ.get(&quot;PACKAGES_PATH&quot;)

And FdfParser.py I've changed the rightward drift of the search attempts for IncludedFile to instead call a private method

 def _FindIncFile(self, IncFileName):
        def _Check(File):
            if not IsValidInclude (File.Path, self.CurrentLineNumber):
                raise Warning(&quot;The include file {} is causing an include loop.&quot;
                              .format(IncludedFile1.Path),
                                      self.FileName,
                                      self.CurrentLineNumber)
            return File
        IncludedFile = NormPath(IncFileName)
        #
        # First search the include file under the same directory as FDF file
        #
        FdfDir = os.path.dirname(self.FileName)
        IncludedFile1 = PathClass(IncludedFile, FdfDir)
        if IncludedFile1.Validate()[0] == 0:
            return _Check(IncludedFile1)
        #
        # Then search the include file under the same directory as DSC file
        #
        PlatformDir = &apos;&apos;
        if GenFdsGlobalVariable.ActivePlatform:
            PlatformDir = GenFdsGlobalVariable.ActivePlatform.Dir
        elif GlobalData.gActivePlatform:
            PlatformDir = GlobalData.gActivePlatform.MetaFile.Dir
        IncludedFile1 = PathClass(IncludedFile, PlatformDir)
        if IncludedFile1.Validate()[0] == 0:
            return _Check(IncludedFile1)
        #
        # Also search file under the WORKSPACE directory
        #
        IncludedFile1 = PathClass(IncludedFile, GlobalData.gWorkspace)
        if IncludedFile1.Validate()[0] == 0:
            return _Check(IncludedFile1)
        for PkgDir in GlobalData.gPackagesPath:
            IncludedFile1 = PathClass(IncludedFile, PkgDir)
            if IncludedFile1.Validate()[0] == 0:
                return _Check(IncludedFile1)
        raise Warning(
                &quot;The include file %s does not exist under below directories: \n%s\n&quot;%(
                    IncludedFile,
                    &quot;\n&quot;.join([
                            os.path.dirname(self.FileName),
                            PlatformDir,
                            GlobalData.gWorkspace,
                            ]+GlobalData.gPackagesPath)),
                self.FileName, self.CurrentLineNumber)

The gPackagesPath is an addition to GlobalData, initialized in CheckEnvVariable in build.py:

    GlobalData.gPackagesPath = mws.PACKAGES_PATH
    GlobalData.gGlobalDefines[&quot;PACKAGES_PATH&quot;] = os.pathsep.join(mws.PACKAGES_PATH)

I still don't have a good understanding why the multithreading fails with PACKAGES_PATH.
I suspect that even pyos.environb doesn't maintain the variable values needed during multithreading, but GenFdsApi generally doesn't look thread-safe with its resetting of globals.
Even with gEnableGenfdsMultiThread = False, -n 2 fails.
If I copy the entire source tree into the workspace instead of keeping output and input separate, path searches don't need to use PACKAGES_PATH and do succeed multithreaded, but that's a hacky solution. There's something off here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority:low Little to no impact. No urgency to fix. type:bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant