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

Remove for/if antipattern in PreBuildInfo::PreBuildInfo. #1362

Merged
merged 2 commits into from Mar 21, 2024

Conversation

BillyONeal
Copy link
Member

The for loop starting on 1798 looked up each entry from its associated table, then had an individual if for each entry in the table. As a result, it wasn't truly being data driven since there was an if for each entry in the table. Moreover, the table had a circular dependency with the code since LOAD_VCVARS_ENV had to come after CHAINLOAD_TOOLCHAIN_FILE. I assume this was done to deduplicate the work of figuring out if the value was set since that is the common block at the top of the loop. Instead of the for/if antipattern I solved that problem by extracting functions.

The function Util::assign_if_set_and_nonempty preserves the table-like behavior for those values which are simple assignments as much as possible. The function Util::value_if_set_and_nonempty is used for the settings that needed additional work.

The for loop starting on 1798 looked up each entry from its associated table, then had an individual if for each entry in the table. As a result, it wasn't truly being data driven since there was an if for each entry in the table. Moreover, the table had a circular dependency with the code since LOAD_VCVARS_ENV had to come after CHAINLOAD_TOOLCHAIN_FILE. I assume this was done to deduplicate the work of figuring out if the value was set since that is the common block at the top of the loop. Instead of the for/if antipattern I solved that problem by extracting functions.

The function Util::assign_if_set_and_nonempty preserves the table-like behavior for those values which are simple assignments as much as possible. The function Util::value_if_set_and_nonempty is used for the settings that needed additional work.
Util::assign_if_set_and_nonempty(external_toolchain_file, cmakevars, CMakeVariableChainloadToolchainFile);
if (auto value = Util::value_if_set_and_nonempty(cmakevars, CMakeVariableBuildType))
{
if (Strings::case_insensitive_ascii_equals(*value, "debug"))
Copy link
Member Author

Choose a reason for hiding this comment

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

This used to be on 1821

Copy link
Contributor

Choose a reason for hiding this comment

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

vcpkg does not really support build type being set to debug only. It will basically error in all the places, post build checks being one of them.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's true. But not looking to make functional changes here.

@Neumann-A
Copy link
Contributor

Will create a merge conflict for #797 which still is not merged for some reason.....

@Neumann-A
Copy link
Contributor

hmm there is already a merge conflict..... I am kind of annoyed that it has not been merged before that happening.....

Comment on lines +1766 to +1774
if (auto value = Util::value_if_set_and_nonempty(cmakevars, CMakeVariableBuildType))
{
if (Strings::case_insensitive_ascii_equals(*value, "debug"))
build_type = ConfigurationType::Debug;
else if (Strings::case_insensitive_ascii_equals(*value, "release"))
build_type = ConfigurationType::Release;
else
Checks::msg_exit_with_message(VCPKG_LINE_INFO, msgUnknownSettingForBuildType, msg::option = *value);
}
Copy link
Contributor

@Neumann-A Neumann-A Mar 7, 2024

Choose a reason for hiding this comment

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

this looks like value_if_set_and_valid (Use lambda as validator?)

Copy link
Contributor

Choose a reason for hiding this comment

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

This would be the same as optional. See my comment above.

Comment on lines 1790 to 1807
load_vcvars_env = !external_toolchain_file.has_value();
if (auto value = Util::value_if_set_and_nonempty(cmakevars, CMakeVariableLoadVcvarsEnv))
{
load_vcvars_env = from_cmake_bool(*value, CMakeVariableLoadVcvarsEnv).value_or_exit(VCPKG_LINE_INFO);
}

if (auto value = Util::value_if_set_and_nonempty(cmakevars, CMakeVariableDisableCompilerTracking))
{
disable_compiler_tracking =
from_cmake_bool(*value, CMakeVariableDisableCompilerTracking).value_or_exit(VCPKG_LINE_INFO);
}

if (Util::value_if_set_and_nonempty(cmakevars, CMakeVariableXBoxConsoleTarget) != nullptr)
{
target_is_xbox = true;
}

Util::assign_if_set_and_nonempty(gamedk_latest_path, cmakevars, CMakeVariableZVcpkgGameDKLatest);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe separate side effects from the value_if_set_and_nonempty and make it a two step process?

Copy link
Member Author

Choose a reason for hiding this comment

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

The reason they're combined here is to preserve the table-like structure for the fields where that is actually possible

}

template<class Map, class Key>
void value_or_default(const Map& map, Key&& key, typename Map::mapped_type&& default_value) = delete;
inline const std::string* value_if_set_and_nonempty(const std::unordered_map<std::string, std::string>& haystack,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use optional here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Optional would imply copying the content, and we don't need to copy the content. The string is already in the unordered_map, so we can just use a pointer to it.

Optional is only necessary in cases where the thing must own the actual data.

Comment on lines +1766 to +1774
if (auto value = Util::value_if_set_and_nonempty(cmakevars, CMakeVariableBuildType))
{
if (Strings::case_insensitive_ascii_equals(*value, "debug"))
build_type = ConfigurationType::Debug;
else if (Strings::case_insensitive_ascii_equals(*value, "release"))
build_type = ConfigurationType::Release;
else
Checks::msg_exit_with_message(VCPKG_LINE_INFO, msgUnknownSettingForBuildType, msg::option = *value);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be the same as optional. See my comment above.

{
const auto it = map.find(static_cast<Key&&>(key));
if (it == map.end())
auto it = haystack.find(needle.to_string());
Copy link
Contributor

Choose a reason for hiding this comment

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

The need to allocate strings for every lookup here seems very unfortunate -- is it an extensive change to push transparency up into the data structures? (replace unordered_map with map, etc).

Copy link
Member Author

Choose a reason for hiding this comment

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

Because I don't have a good understanding of the performance consequences of a change like that, and it isn't on the path for any of the customer facing improvements I'm trying to ship, I'm not considering doing that right now.

…i-pattern

# Conflicts:
#	src/vcpkg/commands.build.cpp
@BillyONeal BillyONeal merged commit 086859d into microsoft:main Mar 21, 2024
5 checks passed
@BillyONeal BillyONeal deleted the bill-hates-for-if-anti-pattern branch March 21, 2024 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants