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

treewide: support structuredAttrs in setup hooks (part 2) #335666

Merged

Conversation

wolfgangwalther
Copy link
Contributor

Description of changes

This is a follow up to #318614, which had a lot more commits initially. After merging the basic changes for stdenv's appendTo, prependTo and new concatTo functions there, I now come back to apply those in more setup hooks.

I intentionally only took a part of long tail of commits I still have for this, so there will be more after this PR. All commits in this PR have one thing in common: I was able to test each of them by building the respective package mentioned in the commit message with __structuredAttrs turned on, similar to how I did it in #318614: The build for this package would fail on master, but succeed on this branch.

For some of the other setup hooks it's not that easy to find examples in nixpkgs, which actually fail with structuredAttrs turned on, so hard to prove the fix works. Thus I left them out for the moment and will bring them back once we have merged this set of changes, increasing the confidence in the general pattern of how the transition to concatTo works.

CC @emilazy @philiptaron @SomeoneSerge @tie

Since the changed setup hooks won't trigger ofborg to mention the maintainers:

Quick recap for those not involved in #318614: That PR added a new function concatTo to allow merging of flags arrays / lists in either string or bash array format. Together with the already existing appendTo and prependTo, this allows to transparently support both building with and without structuredAttrs turned on, without putting checks whether structuredAttrs are enabled or not all over nixpkgs.

Making all the setup hooks support structuredAttrs is the first step to making this the default eventually.

Things done

Of course I built the respective packages themselves - but since the setup hook is not a part of the derivation, those still work. As mentioned above, I built another package for each of the setup hooks, which uses that hook in the build process.

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.11 Release Notes (or backporting 23.11 and 24.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

Copy link
Contributor

@philiptaron philiptaron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't tested it yet. Lots of questions about shellcheck; thanks for adding it in a lot of areas. The overall approach is sound.

pkgs/development/tools/build-managers/build2/setup-hook.sh Outdated Show resolved Hide resolved
pkgs/development/tools/build-managers/gn/setup-hook.sh Outdated Show resolved Hide resolved
pkgs/development/compilers/zig/setup-hook.sh Outdated Show resolved Hide resolved
pkgs/development/compilers/zig/setup-hook.sh Show resolved Hide resolved
pkgs/development/compilers/zig/0.9/setup-hook.sh Outdated Show resolved Hide resolved
pkgs/by-name/ju/just/setup-hook.sh Show resolved Hide resolved
@wolfgangwalther wolfgangwalther force-pushed the structured-attrs-setup-hooks-tested branch from 4fdc570 to d80cd3f Compare August 19, 2024 07:50
@wolfgangwalther
Copy link
Contributor Author

Addressed the first round of comments.

@wolfgangwalther wolfgangwalther force-pushed the structured-attrs-setup-hooks-tested branch from d80cd3f to 185ec47 Compare August 19, 2024 07:51
Copy link
Member

@tie tie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, although the changes introduce some side effects (see comment).

pkgs/by-name/bm/bmake/setup-hook.sh Outdated Show resolved Hide resolved
Copy link
Contributor

@philiptaron philiptaron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the shellcheck issues.

I like @tie's suggestion about

if [[ -v checkFlags ]]; then
  concatTo flagsArray checkFlags
else
  appendToVar flagsArray VERBOSE=y
fi

@wegank wegank added the 12.approvals: 1 This PR was reviewed and approved by one reputable person label Aug 20, 2024
@philiptaron
Copy link
Contributor

philiptaron commented Aug 23, 2024

Is there anything that blocks this from being merged?

I move we merge it, and include it in the new staging-next and let it bake.

@emilazy
Copy link
Member

emilazy commented Aug 23, 2024

We can’t merge a nice‐to‐have Darwin stdenv rebuild into staging-next. Hydra has already built over 10,000 packages. It will have to go in the next cycle.

@SomeoneSerge
Copy link
Contributor

SomeoneSerge commented Aug 23, 2024

Can't contribute much to the discussion above but I read through the diffs ✅

flagsArray ... visible in postX-hooks

I have some concerns over what the flagsArray pattern means for #296544 (example: use part of the cmake build phase and part of the pypa-build phase in the same derivation)

@wolfgangwalther
Copy link
Contributor Author

flagsArray ... visible in postX-hooks

I have some concerns over what the flagsArray pattern means for #296544 (example: use part of the cmake build phase and part of the pypa-build phase in the same derivation)

Do you mean whether flagsArray might leak into other phases, e.g. when calling them in post-hooks? The pattern we used so far is, that each hook creates a clean local flagsArray=(...) first, before using concatTo. So even if it was set before, it should not matter. So I think flagsArray is non-critical.

I will look into the default values thread above again to make sure that it's non-leaking, even in hooks, though.

@wolfgangwalther wolfgangwalther force-pushed the structured-attrs-setup-hooks-tested branch from 185ec47 to 2a30acb Compare August 23, 2024 20:05
Tested samba4 with and without __structuredAttrs.
Tested waylock with and without __structuredAttrs.
Tested build2 itself with and without __structuredAttrs.
Tested swiften with and without __structuredAttrs.
The checkTarget variable is explicitly tested before, but not passed to
scons.

This was apparently missed in the initial commit for that setupHook in
NixOS#50293.
Tested gmad with and without __structuredAttrs.

Building it succeeded on master with structuredAttrs, but the log output
showed that the configure flags were not passed correctly previously.
Tested v8 with and without __structuredAttrs.
Tested qbs with and without __structuredAttrs.
Tested perlPackages.GSSAPI with and without __structuredAttrs.
@wolfgangwalther wolfgangwalther force-pushed the structured-attrs-setup-hooks-tested branch from 9b60c03 to 581aebc Compare August 24, 2024 10:35
@wolfgangwalther
Copy link
Contributor Author

I'm currently looking into how I can fallback to the default better.

I'm currently running a local build through with this check

boost built fine, so pushed. Still building the world to confirm each of the commits still works.

@ofborg ofborg bot added the 10.rebuild-linux-stdenv This PR causes stdenv to rebuild label Aug 24, 2024
Copy link
Contributor

@philiptaron philiptaron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks good to me.

@tie
Copy link
Member

tie commented Aug 25, 2024

IFS="=" read -r name default <<< "$arg"
local -n nameref="$name"
if [[ ! -n "${nameref[@]}" && -n "$default" ]]; then
           targetref+=( "$default" )

@tie do you see any problem with that approach?

It treats an array with a single element that is an empty string as empty 😅
Probably not an issue in practice though.

declare -a arr=( '' )
if [[ -v arr ]]; then
  echo defined
fi
if [[ -z "${arr[@]}" ]]; then
  echo oops
fi
# Output:
# defined
# oops

What do you think about the following check?

# Either variable is not declared,
! type=$(declare -p -- "$name" 2>/dev/null) ||
  # or it is declared but no value is assigned (e.g. empty array),
  [[ -z ${!name+x} ]] ||
  # or it is not an array and is empty.
  [[ ${type#* } != -[aA]* && -z ${!name} ]]

That is, we’d treat empty values (empty arrays and strings) consistently by using the default value.

For example,

set -eu -o pipefail

concatTo() {
    local -n targetref=$1
    shift
    local arg default name type
    for arg; do
        IFS='=' read -r name default <<<"$arg"
        # Append the default value if $name variable is not declared. Note that
        # variable attributes are separate from variable assignment. In addition
        # to that, declare assigns an attribute for name but does not actually
        # create the variable for empty arrays (i.e. `declare -a arr=()`).
        #
        # For compatibility, we treat variables with empty values as undeclared as well.
        if
            # Either variable is not declared,
            ! type=$(declare -p -- "$name" 2>/dev/null) ||
                # or it is declared but no value is assigned (e.g. empty array),
                [[ -z ${!name+x} ]] ||
                # or it is not an array and is empty.
                [[ ${type#* } != -[aA]* && -z ${!name} ]]
        then
            case "$default" in
            '('*)
                # Evaluate array expressions, i.e.
                # `concatTo fooFlags barFlags='(baz)'`.
                #
                # Beware that, just like the regular array assignments, this
                # evaluates Bash code inside the default value. E.g.
                # `declare -a arr='( "$(echo element)" )' && echo "${arr[*]}"` prints
                # `element`.
                local -a defaultArray=$default
                targetref+=("${defaultArray[@]}")
                ;;
            '')
                # Ignores both `undefinedVar=` and `undefinedVar` to avoid
                # appending an empty string for the latter.
                # TODO: perhaps return an error if no default is given and
                # variable is not declared?
                ;;
            *)
                targetref+=("$default")
                ;;
            esac
            continue
        fi

        local -n nameref=$name
        case "${type#* }" in
        -A*)
            echo "concatTo(): ERROR: trying to use concatTo on an associative array." >&2
            return 1
            ;;
        -a*)
            targetref+=("${nameref[@]}")
            ;;
        *)
            if [[ $name == *"Array" ]]; then
                targetref+=("${nameref[@]}")
            else
                # shellcheck disable=SC2206
                targetref+=(${nameref-})
            fi
            ;;
        esac
    done
}


declare -a definedFlags=(b c)
declare -a emptyArray=()
declare emptyString=
declare unboundString
declare string=h
concatTo flagsArray emptyArray=a definedFlags=unused undefinedWithDefaultArray='(d e)' emptyString=f unboundString=g string=unused undefinedWithoutDefault
echo "${flagsArray[@]}"
# Output:
# a b c d e f g h

Note that the code above also allows using arrays as default values, e.g.

concatToVar flagsArray defaultArray='( --foo --bar --baz )'

@wolfgangwalther
Copy link
Contributor Author

@tie do you see any problem with that approach?

It treats an array with a single element that is an empty string as empty 😅

Yes, that's on purpose to not introduce a breaking change.

Note that the code above also allows using arrays as default values, e.g.

concatToVar flagsArray defaultArray='( --foo --bar --baz )'

Interesting. I have only found one use-case, so far, the autoreconf hook. I might look into this later (travelling this week).

@wolfgangwalther
Copy link
Contributor Author

Still building the world to confirm each of the commits still works.

Confirmed that all commits still work and have the intended change for building with structuredAttrs.

@tie
Copy link
Member

tie commented Aug 28, 2024

Yes, that's on purpose to not introduce a breaking change.

Perhaps a check for [[ -z ${nameref[@]:+x} ]] is more clear then, I usually expect ${var[@]} to expand to multiple words (which I don’t think is the case here).

$ bash -e -u -c 'declare -a arr=(); echo "${arr[@]:+x}"'

$ bash -e -u -c 'declare -a arr=(""); echo "${arr[@]:+x}"'

$ bash -e -u -c 'declare -a arr=("" ""); echo "${arr[@]:+x}"'
x
$ bash -e -u -c 'declare -a arr=("" "brr"); echo "${arr[@]:+x}"'
x
$ bash -e -u -c 'declare -a arr=("brr"); echo "${arr[@]:+x}"'
x
$ bash -e -u -c 's=; echo "${s[@]:+x}"'

$ bash -e -u -c 's=brr; echo "${s[@]:+x}"'
x

@philiptaron philiptaron merged commit 0d1b268 into NixOS:staging Aug 28, 2024
26 of 27 checks passed
@philiptaron
Copy link
Contributor

philiptaron commented Aug 28, 2024

@tie, let's finesse it in follow-on PRs. Welcome to the committer team, btw!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants