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

twister: add option to create shorter build paths #41930

Merged
merged 1 commit into from
Feb 22, 2022

Conversation

gopiotr
Copy link
Collaborator

@gopiotr gopiotr commented Jan 18, 2022

Proposition in this PR are strictly connected with problem described here: #41929

Originally when Twister is call by following command:

python scripts\twister --outdir C:\tb --ninja -p nrf5340dk_nrf5340_cpuapp -T samples/subsys/portability/cmsis_rtos_v1/timer_synchronization

It create following build folder: C:\tb\nrf5340dk_nrf5340_cpuapp\samples\subsys\portability\cmsis_rtos_v1\timer_synchronization\sample.portability.cmsis_rtos_v1.timer_synchronization which is problematic for CMake on Windows OS.

Thanks to add --short-build-path option to Twister command:

python scripts\twister --outdir C:\tb --short-build-path --ninja -p nrf5340dk_nrf5340_cpuapp -T samples/subsys/portability/cmsis_rtos_v1/timer_synchronization

Twister create originally build directory, but moreover it create also new directory with shorter path C:\tb\twister_links\test_0 and next create link between those two directories. Thank to that shorter path can be pass to CMake which allow to remove problem with too long path and at once build results can be found on originally longer path.

Without this feature I am not able to use Twister during testing Zephyr's samples and tests on Windows OS.

Fixes #41929

scripts/twister Outdated
@@ -584,6 +584,12 @@ structure in the main Zephyr tree: boards/<arch>/<board_name>/""")
"This directory will be cleaned unless '--no-clean' is set. "
"The '--clobber-output' option controls what cleaning does.")

parser.add_argument(
Copy link
Collaborator

Choose a reason for hiding this comment

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

please put this argument in alpha-beta order, this is starting from s, and shall not put before o

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh, yes, you're right, I replaced it in correct place :)

@@ -1887,7 +1887,10 @@ class TestInstance(DisablePyTestCollectionMixin):
out directory used is <outdir>/<platform>/<test case name>
"""

def __init__(self, testcase, platform, outdir):
# used for creating shorter build paths
shortcut_dir_counter = 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

it is not our practice to add a class variable, why it can not be a instant variable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I agree with you, that this is not the best practice, but I need this variable, which works as a global counter (it is used to create unique folder name) and this value need to be shared between instances of TestInstance class. If you have better idea how to create such global variable, I am open for propositions :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

the class instance only count in one process or twister instance, if you really care about the possible conflect, maybe use filelock would be more safe

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, you are right, I do not predict this case when two Twister instances are run simultaneously (especially when --subset option is used). So I have to refactor this approach. Thank you for your comment!

shortcut_path = os.path.join(shortcuts_dir_path, shortcut_name)
TestInstance.shortcut_dir_counter += 1

os.system(f"mklink /J {shortcut_path} {self.build_dir}")
Copy link
Collaborator

Choose a reason for hiding this comment

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

this would be only works in windows.

Copy link
Collaborator Author

@gopiotr gopiotr Jan 19, 2022

Choose a reason for hiding this comment

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

Yes, it will, but I add check if this code is run on Windows at the beginning of this method:

def create_build_dir_shortcut(self):
        """
        Create build directory with original "long" path. Next take shorter
        path (based on TestInstance class internal counter) and link them with
        original path - create shortcut. At the end replace build_dir to
        shorter path. This action helps to limit path length which can be
        significant during building by CMake on Windows OS.
        """
        if os.name != "nt":  # if OS is not Windows
            logger.warning("Creating shorter build paths available only on "
                           "Windows OS")
            return

--short-build-path can be used only on Windows OS (what is described in option's help). Only on Windows OS exists problem with too long directory paths. So, I do not see any sense to extend this option to Linux OS where this problem does not exist.

hakehuang
hakehuang previously approved these changes Jan 19, 2022
@gopiotr gopiotr added the DNM This PR should not be merged (Do Not Merge) label Jan 19, 2022
@gopiotr gopiotr force-pushed the pr/shorter_build_paths branch 3 times, most recently from db1e4ed to eb7fb10 Compare January 19, 2022 20:58
Comment on lines 2077 to 2083
while not shortcut_created_flag:
shortcut_name = self.get_new_shortcut_name(shortcuts_dir_path)
shortcut_path = os.path.join(shortcuts_dir_path, shortcut_name)
command = ["mklink", "/J", f"{shortcut_path}", f"{self.build_dir}"]
result = subprocess.call(command, shell=True)
if result == 0:
shortcut_created_flag = True
Copy link
Member

Choose a reason for hiding this comment

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

Don't we need any break condition to prevent infinite loops here?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think running multiple twister instances is a real use case as it doesn't bring any value, since twister already handles parallelization internally. Also, each instance will create its own output dir, so if shortcuts are there, there won't be any conflict between shortcuts from multiple instances. Running multiple twister instances for subset is AFAIK used either in parallel on separate agents or on a single agent sequentially.
I am not sure if the proposed mechanism will prevent race conditions which might cause mess in links when build processes run in parallel within a single twister instance.
What do you think about using locks? E.g. the counter variable is define at the twister initialization and has getter/setter methods with a lock. When a shortcut is created, the variable is locked, the counter is increased, shortcut created, and the lock is released? Using such approach might also optimize the code a bit, since you won't need to scan the whole catalog looking for the last number and instead get it from the counter immediately. You can check here how it was implemented for counting test cases: https://github.com/zephyrproject-rtos/zephyr/blob/main/scripts/pylib/twister/twisterlib.py#L94

while not shortcut_created_flag:
shortcut_name = self.get_new_shortcut_name(shortcuts_dir_path)
shortcut_path = os.path.join(shortcuts_dir_path, shortcut_name)
command = ["mklink", "/J", f"{shortcut_path}", f"{self.build_dir}"]
Copy link
Member

Choose a reason for hiding this comment

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

why isn't os.symlink used (or pathlib symlink)?

@gopiotr
Copy link
Collaborator Author

gopiotr commented Jan 26, 2022

With recently changes I move creating shortcuts methods to TestSuite class and I call it in twister file after performing filtration and before run CMake, building and executing. This solution do not demand any additional protection of shortcut_dir_counter variable because this fragment of code is executed before any parallelization. Moreover moving shortcut_dir_counter variable (which is used during creating shortcut names) from TestInstance to the TestSuite class resolves the problem with class variable, because TestSuite instance is created only once during all program running and is able to store this counter among all TestInstance.

why isn't os.symlink used (or pathlib symlink)?

@gmarull I tried to use os.symlink and pathlib symlink but both solutions demand Administrator privilege to create symlink. So I decided to stay with mklink raw command because I do not want to enforce on potential user to run Twister with Administrator privilege.

@PerMac proposed also idea to remove --short-build-path option and introduce creating those shortcuts by default when Twister is run on Windows OS. I think that it could be a good idea, but I would like to get to know other reviewer opinions about it :)

@gopiotr gopiotr removed the DNM This PR should not be merged (Do Not Merge) label Jan 26, 2022
@gopiotr gopiotr requested review from nashif and removed request for nashif January 31, 2022 14:38
@PerMac PerMac self-requested a review February 1, 2022 14:27
PerMac
PerMac previously approved these changes Feb 1, 2022
Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

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

why invent yet another term, shortcut ?

Let's use link, which is a term familiar to developers, after all, that's what we are creating:

There are three types of file links supported in the NTFS file system: hard links, junctions, and symbolic links.

and

A junction (also called a soft link) differs from a hard link in that the storage objects it references are separate directories,

https://docs.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions

and the command is name mklink

Comment on lines 3887 to 3890
if os.name != "nt": # if OS is not Windows
logger.warning("Creating shorter build paths available only on "
"Windows OS")
return
Copy link
Collaborator

Choose a reason for hiding this comment

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

any particular reason this cannot work on other OS'es ?

On macOS / Linux we could create symlinks instead.

That will allow users to pass on --short-build-path regardless of the platform on which they are executing.

"Windows OS")
return

shortcuts_dir_name = "scs" # folder for all shortcuts
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume this suddenly means that no one can create a board named scs.

How about twister_links ?
Reasons:

  • twister is already used for other twister generated files, like twister.csv, twister.log, etc.
    So twister can already today be considered reserved name.
  • _links are clearer in its intention to the reader than scs.

and twister_links are still short, compared to existing behavior.

Comment on lines 3915 to 3920
command = ["mklink", "/J", f"{shortcut_path}", f"{instance.build_dir}"]
subprocess.call(command, shell=True)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should also support ln -s here for other OS'es.

I see no reasons why this new argument should not be supported on all platforms.

if instance.status != "skipped":
self._create_build_dir_shortcut(shortcuts_dir_path, instance)

def _create_build_dir_shortcut(self, shortcuts_dir_path, instance):
Copy link
Collaborator

Choose a reason for hiding this comment

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

why invent yet another term, shortcut ?

Let's use link, which is a term familiar to developers, after all, that's what we are creating:

There are three types of file links supported in the NTFS file system: hard links, junctions, and symbolic links.

and

A junction (also called a soft link) differs from a hard link in that the storage objects it references are separate directories,

https://docs.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions

and the command is name mklink

@tejlmand
Copy link
Collaborator

tejlmand commented Feb 7, 2022

One more question.
I would expect that CMake had to be invoked with the link as its build directory, or does that happen automatically ?

Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

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

One more question. I would expect that CMake had to be invoked with the link as its build directory, or does that happen automatically ?

Guess the answer is here: #41930 (comment)

thank to that shorter path can be pass to CMake

So where in code does this happen ?
Cause I can't spot that location.

@gopiotr
Copy link
Collaborator Author

gopiotr commented Feb 7, 2022

@tejlmand

CMake is call in Twister exactly here:

p = subprocess.Popen(cmd, **kwargs)

Few lines earlier the command with CMake is built and there the build directory (which is the link, when --short-build-path option is enable) is passed:

f'-B{self.build_dir}',

@stephanosio
Copy link
Member

stephanosio commented Feb 13, 2022

If we can predict / detect this situation, then printing a warning together with a hint about --short-build-path would be very nice, but I see that as extension to this PR.

Since this is a very widespread problem when running twister on Windows (thanks to how twister creates a bunch of directories with very long names), I think it would make sense to display that warning by default on Windows when --short-build-path is not specified, regardless of whether a particular run will be affected by the long path issues -- moreover, detecting such conditions would be non-trivial as it can be caused by CMake, GNU Make|Ninja, Binutils/GCC, QEMU or anything that gets invoked by the build system.

We can get rid of this behaviour when all the Windows build tools are updated to support long paths in the future.

Copy link
Member

@stephanosio stephanosio left a comment

Choose a reason for hiding this comment

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

LGTM

Verified working on Linux, macOS and Windows build environments:
https://github.com/stephanosio/gha-runner-zephyr-test/actions/runs/1835838884

gopiotr added a commit to gopiotr/sdk-zephyr that referenced this pull request Feb 14, 2022
squash! [nrf fromlist] twister: add option to create shorter build paths

This changes was introduced to this open Upstream PR:
zephyrproject-rtos/zephyr#41930

Signed-off-by: Piotr Golyzniak <piotr.golyzniak@nordicsemi.no>
@carlescufi
Copy link
Member

LGTM

Verified working on Linux, macOS and Windows build environments: https://github.com/stephanosio/gha-runner-zephyr-test/actions/runs/1835838884

Thanks for testing @stephanosio!

Copy link
Member

@carlescufi carlescufi left a comment

Choose a reason for hiding this comment

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

LGTM

mbolivar-nordic pushed a commit to nrfconnect/sdk-zephyr that referenced this pull request Feb 14, 2022
squash! [nrf fromlist] twister: add option to create shorter build paths

This changes was introduced to this open Upstream PR:
zephyrproject-rtos/zephyr#41930

Signed-off-by: Piotr Golyzniak <piotr.golyzniak@nordicsemi.no>
Copy link
Member

@nashif nashif left a comment

Choose a reason for hiding this comment

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

Does not work for me on Linux...

i9:zephyr((HEAD detached at upstream/pull/41930)): twister -T samples/hello_world/ --short-build-path
Renaming output directory to /home/nashif/Work/zephyrproject/zephyr/twister-out.2
INFO    - Zephyr version: v3.0.0-rc3-2-g5b7e539480ea
INFO    - JOBS: 20
INFO    - Using 'zephyr' toolchain.
INFO    - Selecting default platforms per test case
INFO    - Building initial testcase list...
INFO    - 1 test scenarios (39 configurations) selected, 14 configurations discarded due to filters.
INFO    - Adding tasks to the queue...
INFO    - Added initial list of jobs to queue

ERROR   - qemu_malta                samples/hello_world/sample.basic.helloworld        FAILED: Build failure
ERROR   - see: /home/nashif/Work/zephyrproject/zephyr/twister-out/qemu_malta/samples/hello_world/sample.basic.helloworld/build.log
INFO    - Total complete:    1/  25   4%  skipped:   14, failed:    1
ERROR   - qemu_nios2                samples/hello_world/sample.basic.helloworld        FAILED: Build failure
ERROR   - see: /home/nashif/Work/zephyrproject/zephyr/twister-out/qemu_nios2/samples/hello_world/sample.basic.helloworld/build.log
INFO    - Total complete:    2/  25   8%  skipped:   14, failed:    2
ERROR   - qemu_malta_be             samples/hello_world/sample.basic.helloworld        FAILED: Build failure
ERROR   - see: /home/nashif/Work/zephyrproject/zephyr/twister-out/qemu_malta_be/samples/hello_world/sample.basic.helloworld/build.log
INFO    - Total complete:    3/  25  12%  skipped:   14, failed:    3
ERROR   - m2gl025_miv               samples/hello_world/sample.basic.helloworld        FAILED: Build failure
ERROR   - see: /home/nashif/Work/zephyrproject/zephyr/twister-out/m2gl025_miv/samples/hello_world/sample.basic.helloworld/build.log
INFO    - Total complete:    4/  25  16%  skipped:   14, failed:    4
ERROR   - qemu_arc_em               samples/hello_world/sample.basic.helloworld        FAILED: Build failure
ERROR   - see: /home/nashif/Work/zephyrproject/zephyr/twister-out/qemu_arc_em/samples/hello_world/sample.basic.helloworld/build.log

error log:

-- Found assembler: /home/nashif/bin/zephyr-sdk-0.13.2/nios2-zephyr-elf/bin/nios2-zephyr-elf-gcc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/nashif/Work/zephyrproject/zephyr/twister-out/twister_links/test_21
[  1%] Generating misc/generated/syscalls.json, misc/generated/struct_tags.json
[  2%] Built target parse_syscalls_target
gmake[2]: *** No rule to make target '../../../scripts/gen_kobject_list.py', needed by 'zephyr/include/generated/kobj-types-enum.h'.  Stop.
gmake[1]: *** [CMakeFiles/Makefile2:2643: zephyr/CMakeFiles/kobj_types_h_target.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

@gopiotr
Copy link
Collaborator Author

gopiotr commented Feb 15, 2022

Does not work for me on Linux...

Hmmmmm, yes, now I observe, that --short-build-path option works only with --ninja flag. So far I tested this option only with Ninja generator - due to the fact, that only this one generator is available on Windows OS. I have to find what is source of this problem that simple Unix Makefiles generator do not work with links.

@gopiotr
Copy link
Collaborator Author

gopiotr commented Feb 15, 2022

@nashif I added checking if --ninja argument is passed when --short-build-path option is chosen. I added also clear information in --short-build-path description, that --ninja argument is required in this case.

I tried to find if there is any solution to pass directory link to Unix Makefiles generator, but I did not find such information. So far it seems that it is only available with Ninja generator.

else: # for Linux and MAC OS
os.symlink(instance.build_dir, link_path)

# Here symbolic link is replaced with original build directory. It will
Copy link
Member

Choose a reason for hiding this comment

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

isn't this the other way round?

original build directory is replaced with the symbolic link...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, you are right - I corrected it.

@nashif
Copy link
Member

nashif commented Feb 17, 2022

@nashif I added checking if --ninja argument is passed when --short-build-path option is chosen. I added also clear information in --short-build-path description, that --ninja argument is required in this case.

ok, that works.

Add possibility to create shorter build paths when --short-build-path
option is enabled. This can help during run Twister on Windows OS and
building programs by CMake which has a problem when paths with building
files are too long. The solution based on symbolic links which help to
connect "traditional" long paths with shorter one like:
"twister_links/test_0".

Fixes zephyrproject-rtos#41929

Signed-off-by: Piotr Golyzniak <piotr.golyzniak@nordicsemi.no>
@stephanosio stephanosio added this to the v3.1.0 milestone Feb 21, 2022
@nashif nashif merged commit b618f4b into zephyrproject-rtos:main Feb 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Twister Twister bug The issue is a bug, or the PR is fixing a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

twister: problem with build specific samples on Windows OS
8 participants