Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Compare user input for multiple dependency build variants - #16600

Merged
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args
Aug 10, 2023
Merged

Compare user input for multiple dependency build variants#16600
andrewrk merged 5 commits into
ziglang:masterfrom
mattnite:compare-dependency-args

Conversation

@mattnite

@mattnitemattnite commented Jul 29, 2023

Copy link
Copy Markdown
Contributor

If I try to build a dependency with different arguments today, only one version of that dependency is built, when it should build every variant. Here's a simplified example, I've imported the echo dependency twice, but in each scenario I've specified the dependency with different optimization modes.

// build.zigconstdep_variant_1=b.dependency("echo", .{
.target=target,
.optimize=.Debug,
});
constdep_variant_2=b.dependency("echo", .{
.target=target,
.optimize=.ReleaseSafe,
});

I'll now write a program to print the value returned by get_optimization() in the echo static library of the echo dependency, and compile it twice, each using one of the dependency variants:

// build.zig continued...constprogram_variant_1=b.addExecutable(.{
.name="i_should_print_debug",
.source_file= .{ .path="src/main.zig" },
});
program_variant_1.addModule(dep_variant_1.module("echo"));
b.installArtifact(program_variant_1);
constprogram_variant_2=b.addExecutable(.{
.name="i_should_print_release_safe",
.source_file= .{ .path="src/main.zig" },
});
program_variant_2.addModule(dep_variant_2.module("echo"));
b.installArtifact(program_variant_2);

However when I run both programs:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
Debug

:(

Alright Andrew, I'll bite

walking down the b.dependency() call stack there's a shiny lil todo:

if (b.initialized_deps.get(build_root_string)) |dep| {
// TODO: check args are the samereturndep;
}

To fill in this TODO, the args of different Builds of the same dependency need to be compared. This patch generates a Build's user_input_options here for comparison with existing dependencies. If it's unique then the options are passed down to the newly created Build.

The other piece of work, and related TODO, was generating the hash for the install prefix. Which now looks like this:

varhash=b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when// implementation is modified in a non-backwards-compatible way.hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);
hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);
// TODO additionally update the hash with `args`.constdigest=hash.final();
constinstall_prefix=tryb.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});

AFAIK, it's possible for entries in aUserInputOptionsMap to have different orders depending on order of insertion. hashUserInputOptionsmap orders all values recursively -- TIL there could be maps in the user input option -- and then hashes everything.

Note anyone reviewing please take a look at the hashing of different user values like flag, it seems to me that just hashing the name would be good enough? I'm also not sure if used should be included in hashing as well.

I hope you're happy

Now my programs print:

mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_debug
Debug
mattnite@Matts-MacBook-Pro ~/c/dependency-variants (main)> ./zig-out/bin/i_should_print_release_safe
ReleaseSafe

The repos to reproduce this are dependency-variants and echo.

Comment threadlib/std/Build.zig Outdated
}) catch @panic("OOM");
}

std.sort.insertion(Pair, ordered.items, {}, Pair.lessThan);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why insertion sort rather than sortUnstable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tbh it was the first one that came up in code completion. I didn't give this choice much thought because I doubt we'll see N approach even 1000.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK please do change it to std.mem.sortUnstable. If you require stable sort, std.mem.sort is the way to go, and the time to use std.sort.insertion is pretty much never.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

Comment threadlib/std/Build.zig Outdated
hash.add(@as(u32, 0xd8cb0055));
hash.addBytes(b.dep_prefix);

hashUserInputOptionsMap(b.allocator, b.user_input_options, &hash);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks like you solved that TODO below :)

Comment threadlib/std/Build.zig Outdated
Comment on lines +1730 to +1732
if (userInputOptionsMapsAreSame(user_input_options, dep.builder.user_input_options)) {
return dep;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmm I think I see a big problem here. This is correctly checking if a value in a hash map can be re-used, but then at the end of the function it will put the new value into the hash map, overwriting the old one. In other words, it does not successfully deduplicate with more complicated set of inputs. If we passed ABA, for example, it would create two A's instead of one A and one B.

I think you already created all the glue code that is needed to solve this; the initialized_deps hash map needs to key on not only the build_root_string but on the user input options as well. That hash map is exclusively used in this function, so you are free to change it to this function's needs.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for pointing that out, I completely missed it. I’ve added user input to the key of initialized_deps.

@mitchellh

mitchellh commented Aug 4, 2023

Copy link
Copy Markdown
Contributor

Just noting this is blocking my terminal (Ghostty) from adopting the package manager. We build a universal macOS binary as part of the Mac build and this issue means that we build two of the same arches in a row rather than two distinct ones! 😄

Thanks so much @mattnite for fixing this. :)

@mitchellhmitchellh mentioned this pull request Aug 4, 2023
6 tasks
@ikskuh

Copy link
Copy Markdown
Contributor

Another use case:
In AshetOS i build a fat library in a way that i can use it for both the host to make an os image as well as embed the library into the OS to actually read that file system.

The os isnt using the package manager yet, so i didnt encounter that problem

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mattnite@mitchellh@ikskuh@andrewrk