Skip to content

extensions.cmake: need unique strings, not random ones #14776

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

Merged
merged 1 commit into from
Mar 28, 2019

Conversation

marc-hb
Copy link
Collaborator

@marc-hb marc-hb commented Mar 21, 2019

  1. To support being called multiple times, the function
    zephyr_library_compile_options() uses unique
    options_interface_lib_${random} names. These only need to be unique, not
    random. So replace them with a simple MD5 hash of the ${item} argument.

To reproduce quickly run:
sanitycheck -b -p qemu_xtensa --tag shell
... a few times and compare the output directories.

This bug sits for now at the top of the list of build reproducibility
issues the most bizarre and difficult to root cause. When running
sanitycheck over a large sample of configurations it was affecting
only qemu_xtensa/**/zephyr/arch/arch/xtensa/core/startup/Makefile
files (and their corresponding CMakeFiles/TargetDirectories.txt),
randomly ordering the following three Make targets and only these
three: rebuild_cache, edit_cache, arch__xtensa__core__startup.

The key to root causing was cmake --trace-expand which prints the random
string.

  1. generate_unique_target_name_from_filename() also generated a random
    string for the same uniqueness reason. This one was easier to root cause
    and reproduce with: sanitycheck --tag gen_inc_file

Signed-off-by: Marc Herbert [email protected]

@marc-hb marc-hb added Enhancement Changes/Updates/Additions to existing features area: Build System labels Mar 21, 2019
1. To support being called multiple times, the function
zephyr_library_compile_options() uses unique
options_interface_lib_${random} names. These only need to be unique, not
random. So replace them with a simple MD5 hash of the ${item} argument.

To reproduce quickly run:
  sanitycheck -b -p qemu_xtensa --tag shell
... a few times and compare the output directories.

This bug sits for now at the top of the list of build reproducibility
issues the most bizarre and difficult to root cause. When running
sanitycheck over a large sample of configurations it was affecting
only qemu_xtensa/**/zephyr/arch/arch/xtensa/core/startup/Makefile
files (and their corresponding CMakeFiles/TargetDirectories.txt),
randomly ordering the following three Make targets and only these
three: rebuild_cache, edit_cache, arch__xtensa__core__startup.

The key to root causing was cmake --trace-expand which prints the random
string.

2. generate_unique_target_name_from_filename() also generated a random
string for the same uniqueness reason. This one was easier to root cause
and reproduce with: sanitycheck --tag gen_inc_file

Signed-off-by: Marc Herbert <[email protected]>
@marc-hb marc-hb force-pushed the cmake-unique-not-random branch from f7258dc to 74783a7 Compare March 21, 2019 22:01
@marc-hb marc-hb changed the title extensions.cmake: need a unique string, not a random one extensions.cmake: need unique strings, not random ones Mar 21, 2019
@marc-hb marc-hb added the area: Toolchains Toolchains label Mar 21, 2019
@codecov-io
Copy link

Codecov Report

Merging #14776 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master   #14776   +/-   ##
=======================================
  Coverage   52.51%   52.51%           
=======================================
  Files         309      309           
  Lines       45048    45048           
  Branches    10419    10419           
=======================================
  Hits        23656    23656           
  Misses      16584    16584           
  Partials     4808     4808

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update cb44b7e...74783a7. Read the comment docs.

Copy link
Contributor

@mbolivar mbolivar left a comment

Choose a reason for hiding this comment

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

LGTM if it passes CI

@galak
Copy link
Collaborator

galak commented Mar 28, 2019

Would appreciate @SebastianBoe review on this.

@nashif nashif added this to the v1.14.0 milestone Mar 28, 2019
@galak galak merged commit 915a353 into zephyrproject-rtos:master Mar 28, 2019
@@ -1219,7 +1219,7 @@ function(generate_unique_target_name_from_filename filename target_name)
string(REPLACE "." "_" x ${basename})
string(REPLACE "@" "_" x ${x})

string(RANDOM LENGTH 8 random_chars)
string(MD5 unique_chars ${filename})
Copy link
Collaborator

@SebastianBoe SebastianBoe Apr 1, 2019

Choose a reason for hiding this comment

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

Is filename relative or absolute?

Can we get into trouble if we invoke 'generate_unique_target_name_from_filename' twice on two different generated files with the same relative address?

If not, then this looks good :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for questioning this. I've done some research and experimentation. I think this collision issue:

  • might be possible in theory,
  • is extremely unlikely in practice,
  • if ever, will trigger a very clear error message like this one:
CMake Error at zephyr/cmake/extensions.cmake:512 (add_custom_target):
  add_custom_target cannot create target
  "gen_lets_encrypt_x3_der_inc_7ea3a619643a01cb9f7b7914adc3ad38" because
  another target with the same name already exists.  The existing target is a
  custom target created in source directory
  "zephyr/samples/net/sockets/big_http_download".
  See documentation for policy CMP0002 for more details.
Call Stack (most recent call first):
  src2/CMakeLists.txt:7 (generate_inc_file_for_target)

To trigger the above error message I hacked samples/net/sockets/big_http_download/ and invoked generate_inc_file_for_target() with the same include/generated//lets_encrypt_x3.der.inc relative argument twice. This is really not how CMakeLists.txt files are normally written because CMake generally hates/discourages/deprecates relative paths https://cmake.org/cmake/help/v3.3/variable/CMAKE_USE_RELATIVE_PATHS.html (gone in 3.4)

I scanned the current code base. There are currently eight places where this function is used:

  1. cfb.cmake: generate_cfb_font_for_target()
samples/display/cfb_custom_font/
  1. extensions.cmake: generate_inc_file_for_target()
samples/net/sockets/big_http_download/
samples/net/sockets/dumb_http_server/
samples/net/sockets/echo_client/
samples/net/sockets/echo_server/
samples/net/sockets/http_get/
soc/arm/nxp_lpc/lpc54xxx/
tests/application_development/gen_inc_file/

I traced these invocations and they all use absolute paths because they all rely after a (scary...) number of indirections on MAKE_CURRENT_BINARY_DIR. CMake's love of absolute paths makes sure CMAKE_CURRENT_BINARY_DIR is one https://cmake.org/cmake/help/v3.13/variable/CMAKE_CURRENT_BINARY_DIR.html

Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thank you for the thorough analysis. I agree with your findings and conclusion that this is safe.

string(RANDOM random)
set(lib_name options_interface_lib_${random})
string(MD5 uniqueness ${item})
set(lib_name options_interface_lib_${uniqueness})
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if we add the same item twice?

Expected behaviour would for it to be silently added once due to CMake's de-duplication pass. But now I expect we will get an obscure error.

Perhaps we need to add our own "if not exists" check to silently de-duplicate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You're right, well-spotted and thx! I filed bug #15093 and submitted de-duplication fix #15094, thx for reviewing.

I also temporarily reverted this for testing and cmake is indeed de-duplicating identical compile options coming from multiple pseudo-libraries.

@marc-hb marc-hb deleted the cmake-unique-not-random branch April 1, 2019 19:17
marc-hb added a commit to marc-hb/zephyr that referenced this pull request Apr 4, 2019
PR zephyrproject-rtos#14776 / commit 915a353 changed function
zephyr_library_compile_options() to use MD5 instead of RANDOM for build
reproduction reasons. As later predicted by Sebastian Bøe in the PR
(thx), the names generated for these pseudo-libraries won't be unique
anymore if the exact same flag gets added more than once.

To reproduce the issue, simply add the following two lines in some
CMakeLists.txt file like samples/hello_world/CMakeLists.txt:

zephyr_library_compile_options("-Dfubar=barfu")
zephyr_library_compile_options("-Dfubar=barfu")

cmake will then fail like this:

 CMake Error at zephyr/cmake/extensions.cmake:403 (add_library):
  add_library cannot create target
  "options_interface_lib_e689fdb7eb15d801c2f95dd61301fc5e" because
  another target with the same name already exists.  The existing
  target is an interface library created in source directory
  "zephyr/samples/hello_world".  See documentation for
  policy CMP0002 for more details.
 Call Stack (most recent call first):
  CMakeLists.txt:9 (zephyr_library_compile_options)

As requested by Sebastian, silently discard duplicate options just like
CMake does.

Fixes zephyrproject-rtos#15093

Signed-off-by: Marc Herbert <[email protected]>
nashif pushed a commit that referenced this pull request Apr 4, 2019
PR #14776 / commit 915a353 changed function
zephyr_library_compile_options() to use MD5 instead of RANDOM for build
reproduction reasons. As later predicted by Sebastian Bøe in the PR
(thx), the names generated for these pseudo-libraries won't be unique
anymore if the exact same flag gets added more than once.

To reproduce the issue, simply add the following two lines in some
CMakeLists.txt file like samples/hello_world/CMakeLists.txt:

zephyr_library_compile_options("-Dfubar=barfu")
zephyr_library_compile_options("-Dfubar=barfu")

cmake will then fail like this:

 CMake Error at zephyr/cmake/extensions.cmake:403 (add_library):
  add_library cannot create target
  "options_interface_lib_e689fdb7eb15d801c2f95dd61301fc5e" because
  another target with the same name already exists.  The existing
  target is an interface library created in source directory
  "zephyr/samples/hello_world".  See documentation for
  policy CMP0002 for more details.
 Call Stack (most recent call first):
  CMakeLists.txt:9 (zephyr_library_compile_options)

As requested by Sebastian, silently discard duplicate options just like
CMake does.

Fixes #15093

Signed-off-by: Marc Herbert <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Build System area: Toolchains Toolchains Enhancement Changes/Updates/Additions to existing features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants