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

makefiles/docker.inc.mk: Use directories in RIOT when possible #9646

Merged

Conversation

cladmi
Copy link
Contributor

@cladmi cladmi commented Jul 27, 2018

Contribution description

When building with docker, only mount the directories that are outside of RIOT.
This makes RIOT_FILE_RELATIVE be correctly relative to RIOTBASE when possible.

It also handles correctly EXTERNAL_MODULE_DIRS.

It fixes:

-DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotproject/tests/libfixmath_unittests/bin/pkg/stm32f0discovery/libfixmath/libfixmath/fix16_str.c\"

To

-DRIOT_FILE_RELATIVE=\"tests/libfixmath_unittests/bin/pkg/stm32f0discovery/libfixmath/libfixmath/fix16_str.c\"

This fixes build size issues when RIOT_FILE_RELATIVE was used.

I also fixed BUILDRELPATH when building in docker for an example outside of RIOT to allow testing. (I could split this outside if wanted).

Testing

Build hello-world with docker it should work and you can look at the list of -v and -e options given to docker.

Copy hello-world outside of RIOT, update RIOTBASE in the makefile to point to RIOT and build with docker. It should also work and you can notice that a new -v and -e options are given to docker.

I did not test with external cpu/board/make.

Now more advanced tests are described in these two comments:

Reviewing

I finished this a bit in a friday evening rush so would definitely need a second read.

Issues/PRs references

Tracking issue #9645
#9507

@cladmi cladmi added Area: build system Area: Build system Area: toolchain Area: toolchains; everything related to compilation, libc, linking, … labels Jul 27, 2018
@cladmi cladmi added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Jul 27, 2018
@cladmi
Copy link
Contributor Author

cladmi commented Jul 30, 2018

Another test is building RIOT Tutorials, with task01.
The application is not using a relative path as it is outside of riot and it is handled properly.

make BUILD_IN_DOCKER=1 DOCKER="sudo docker" QUIET=0 clean all | grep 'RIOT_FILE_RELATIVE=\\"/'
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotproject/task-01/main.c\" \

Where in master I had cpu and board using absolute paths too

make BUILD_IN_DOCKER=1 DOCKER="sudo docker" QUIET=0 clean all | grep 'RIOT_FILE_RELATIVE=\\"/'
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotproject/task-01/main.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotboard/native/board_init.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotboard/native/drivers/native-led.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/panic.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/async_read.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/syscalls.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/irq_cpu.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/native_cpu.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/startup.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/periph/uart.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/periph/pm.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/periph/gpio.c\" \
        -DRIOT_FILE_RELATIVE=\"/data/riotbuild/riotcpu/native/vfs/native_vfs.c\" \

I added a sub variable DOCKER_VOLUMES_AND_ENV_EXTERNAL to be able to debug what external directories are being mounted.

Inside examples/hello-world I get nothing:

make BUILD_IN_DOCKER=1 DOCKER="sudo docker"  info-debug-variable-DOCKER_VOLUMES_AND_ENV_EXTERNAL

And from an external application, I get the external 'riotproject':

make BUILD_IN_DOCKER=1 DOCKER="sudo docker"  info-debug-variable-DOCKER_VOLUMES_AND_ENV_EXTERNAL
-v /home/harter/work/git/hello-world:/data/riotbuild/riotproject -e riotproject=/data/riotbuild/riotproject

Copy link
Member

@jnohlgard jnohlgard left a comment

Choose a reason for hiding this comment

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

Looks like a good improvement, but I can't do a proper review until I am back from holidays.

# Add volumes for possible outside directories

DOCKER_RIOT_DIRS = riotcpu riotboard riotmake riotproject
# Names with uppercase
Copy link
Member

Choose a reason for hiding this comment

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

This needs a better comment. I think this is a mapping from directory name inside the docker container to environment variable name for the path outside docker?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, a better comment would help. My understanding is:

It is RIOT directories that can be remapped outside of the RIOT repository.
When mounting volumes in docker, if they are not in the RIOT repository, they need to be mounted as well to be accessible and the environment variable set to the mounted volume.

However, I just thought that it does not correctly handle if someone puts a CPU inside his RIOT repository in another directory. Because the environment variable should be set anyway.
I will fix this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed this as there are not that many variables and was more confusing than anything.

@cladmi cladmi added the State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet label Aug 9, 2018
@cladmi cladmi force-pushed the pr/make/docker/fix_relative_build_directory branch 2 times, most recently from 8141d1b to 99e8f35 Compare September 6, 2018 16:03
@cladmi
Copy link
Contributor Author

cladmi commented Sep 6, 2018

I found out my previous solution was wrong as I was not setting RIOTCPU for variables inside the repository and it could be in a different directory than the original one.

So now everything is based on a path_in_docker function that returns the path directories of the variable will have in docker.

Then another function that takes care of doing the defines.

I do not really know how to test external CPU/BOARD even more that right now they are not working that much.

So I think if building normal applications still work and building an external application with external modules work it will be ok.
I will find simple tests to explain.

@cladmi
Copy link
Contributor Author

cladmi commented Sep 6, 2018

Handled empty variable and and relative path, docker daemon does not like mounting volumes with non absolute path.

@cladmi
Copy link
Contributor Author

cladmi commented Sep 6, 2018

Testing the relative for application directory

We can see the -w /data/riotbuild/riotbase/example/hello-world.
In master it is -w '/data/riotbuild/riotproject/examples/hello-world/'

RIOT/examples/hello-world$ make BUILD_IN_DOCKER=1      
          
Launching build container using image "riot/riotbuild:latest".
docker run --rm -t -u "$(id -u)" \
    -v /etc/localtime:/etc/localtime:ro -v '/home/harter/work/git/RIOT:/data/riotbuild/riotbase' -e 'RIOTBASE=/data/riotbuild/riotbase' -e 'CCACHE_BASEDIR=/data/riotbuild/riotbase' -e 'RIOTPROJECT=/data/riotbuild/riotbase' -e 'RIOTCPU=/data/riotbuild/riotbase/cpu' -e 'RIOTBOARD=/data/riotbuild/riotbase/boards' -e 'RIOTMAKE=/data/riotbuild/riotbase/makefiles'  \
     \
    -w '/data/riotbuild/riotbase/examples/hello-world/' \
    'riot/riotbuild:latest' make   

Testing an external application

In another RIOT repository, for me RIOT_test run an external build.
bin directory should be removed between building with master and this PR because of the '.d' files.

RIOT_test/examples/hello-world$ RIOTBASE=${HOME}/work/git/RIOT make BUILD_IN_DOCKER=1  
Launching build container using image "riot/riotbuild:latest".
docker run --rm -t -u "$(id -u)" \
    -v /etc/localtime:/etc/localtime:ro -v '/home/harter/work/git/RIOT:/data/riotbuild/riotbase' -e 'RIOTBASE=/data/riotbuild/riotbase' -e 'CCACHE_BASEDIR=/data/riotbuild/riotbase' -v '/home/harter/work/git/RIOT_test:/data/riotbuild/riotproject' -e 'RIOTPROJECT=/data/riotbuild/riotproject' -e 'RIOTCPU=/data/riotbuild/riotbase/cpu' -e 'RIOTBOARD=/data/riotbuild/riotbase/boards' -e 'RIOTMAKE=/data/riotbuild/riotbase/makefiles'  \
     \
    -w '/data/riotbuild/riotproject/examples/hello-world/' \
    'riot/riotbuild:latest' make   

Testing an external application with EXTERNAL_MODULE_DIRS

Create two external modules just to execute make inside

mkdir -p external external2
echo 'all:' | tee external/Makefile | tee external2/Makefile
RIOT_test/examples/hello-world$ RIOTBASE=${HOME}/work/git/RIOT make BUILD_IN_DOCKER=1 EXTERNAL_MODULE_DIRS="external external2"                     
               
Launching build container using image "riot/riotbuild:latest".
docker run --rm -t -u "$(id -u)" \
    -v /etc/localtime:/etc/localtime:ro -v '/home/harter/work/git/RIOT:/data/riotbuild/riotbase' -e 'RIOTBASE=/data/riotbuild/riotbase' -e 'CCACHE_BASEDIR=/data/riotbuild/riotbase' -v '/home/harter/work/git/RIOT_test:/data/riotbuild/riotproject' -e 'RIOTPROJECT=/data/riotbuild/riotproject' -e 'RIOTCPU=/data/riotbuild/riotbase/cpu' -e 'RIOTBOARD=/data/riotbuild/riotbase/boards' -e 'RIOTMAKE=/data/riotbuild/riotbase/makefiles'  -v '/home/harter/work/git/RIOT_test/examples/hello-world/external:/data/riotbuild/external/external'  -v '/home/harter/work/git/RIOT_test/examples/hello-world/external2:/data/riotbuild/external/external2' \
     \
    -w '/data/riotbuild/riotproject/examples/hello-world/' \
    'riot/riotbuild:latest' make   'EXTERNAL_MODULE_DIRS=/data/riotbuild/external/external /data/riotbuild/external/external2'
Building application "hello-world" for "native" with MCU "native".

"make" -C /data/riotbuild/external/external
make[2]: Nothing to be done for 'all'.
"make" -C /data/riotbuild/external/external2
make[2]: Nothing to be done for 'all'.

We can see there are two volumes mount for the external modules, and the EXTERNAL_MODULE_DIRS= definition on the command line to be sure it overwrites the values parsed after that could be absolute.

@cladmi cladmi removed the State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet label Sep 6, 2018
@cladmi
Copy link
Contributor Author

cladmi commented Sep 6, 2018

Not WIP anymore for me.

@cladmi
Copy link
Contributor Author

cladmi commented Sep 6, 2018

When #8987 will get merged, there will be a test with one external module dir (not multiple but it is already something).

@tcschmidt
Copy link
Member

@gebart do you mind continuing?

Copy link
Member

@jnohlgard jnohlgard left a comment

Choose a reason for hiding this comment

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

There are probably a few variables in docker.inc.mk which do not need to be exported. They date back to when I wrote the initial Docker integration a few years ago.

Tested locally with some external board and it seems to be working, but I don't fully understand all the template magic

#
# $1 = directories (can be a list of relative files)
# $2 = docker remap base directory (defaults to DOCKER_BUILD_ROOT)
# $3 = mapname (defaults to $(notdir $d))
Copy link
Member

Choose a reason for hiding this comment

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

what is $d?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By default it is the basename of the directory in $1. I will update the docs.

#
# For each directory:
# * if inside, returns $(DOCKER_RIOTBASE)/<relative_path_in_riotbase>
# * if outside, returns <docker remapbase>/<mapname>
Copy link
Member

Choose a reason for hiding this comment

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

inside/outside what?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Inside and outside RIOTBASE I will update

DOCKER_VOLUMES_AND_ENV += $(call docker_volume_and_env,RIOTMAKE,,riotmake)

# Remap external module directories. Not handled if they have common dirnames
ifneq ($(words $(sort $(notdir $(EXTERNAL_MODULE_DIRS)))),$(words $(sort $(EXTERNAL_MODULE_DIRS))))
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 understand this check. What does it do, and why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As we are mapping directories outside of docker to directories in docker all stored in a common base directory, without copying the whole tree hierarchy, they could have collision in names:

EXTERNAL_MODULE_DIRS = relative/to/dir/a another/directory/also/called/a

We would then try to map both to external/a.

For the documentation, the error line may be clearer. I could duplicate it to the comment.

This is a limitation with my implementation, maybe I could do another mapping than this.

Copy link
Contributor

Choose a reason for hiding this comment

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

without copying the whole tree hierarchy

Would it make sense to copy the whole tree hierarchy? (in another PR of course)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could introduce difference between build machine when building in docker which is something I would not want.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I agree, though it would be useful for the user (who knows what kind of weird setup they may have).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right now, I think nobody uses EXTERNAL_MODULE_DIRS and it was not even exported to docker before.

-e 'RIOTCPU=$(DOCKER_BUILD_ROOT)/riotcpu' \
-e 'RIOTBOARD=$(DOCKER_BUILD_ROOT)/riotboard' \
-e 'RIOTMAKE=$(DOCKER_BUILD_ROOT)/riotmake' \
-e 'RIOTPROJECT=$(DOCKER_BUILD_ROOT)/riotproject' \
Copy link
Member

Choose a reason for hiding this comment

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

👍

$(DOCKER_ENVIRONMENT_CMDLINE) \
-w '$(DOCKER_BUILD_ROOT)/riotproject/$(BUILDRELPATH)' \
-w '$(DOCKER_APPDIR)' \
'$(DOCKER_IMAGE)' make $(DOCKER_MAKECMDGOALS) $(DOCKER_OVERRIDE_CMDLINE)
Copy link
Member

Choose a reason for hiding this comment

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

An idea to the future: Propagate the job count from the top level make to the docker make: make -j9 etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried in the past to get the job count from the main makefile but could not :/
I think we only have access to it from a sub make instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we have it from within the target execution environment though as it is passed to sub make instances.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed MAKEFLAGS has it when in the execution environment, and we could extract with a $(filter -j%,$(MAKEFLAGS)) or alike.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some local tested solution for this, configured from outside so re-using DOCKER_MAKECMDGOALS.

# Extract parallel option
# The number of jobs is only available after make 4.2
# The value will not be available in make but in the targets body
MAKE_PARALLEL = $(filter -j -j%,$(MAKEFLAGS))
DOCKER_MAKECMDGOALS += $(MAKE_PARALLEL)

We would get -j even when saying -j2 for make before 4.2 so would only pass -j in ubuntu bionic. But it could still be good.

@cladmi
Copy link
Contributor Author

cladmi commented Oct 11, 2018

Tested locally with some external board and it seems to be working, but I don't fully understand all the template magic

I can update the first description in a generic way of what I do and why, independently of the actual template implementation.

@cladmi
Copy link
Contributor Author

cladmi commented Oct 17, 2018

I pushed some documentation updates and renames.

I did not tested them as I do not have docker on this computer. I will do when I have access to the other one.

@miri64
Copy link
Member

miri64 commented Dec 18, 2018

What's the hold-up on this already ACK'd PR?

@jcarrano
Copy link
Contributor

It's missing a squash and not it has a conflict. After I acked it we took a long detour (caused in great part by #10344 )

@cladmi cladmi force-pushed the pr/make/docker/fix_relative_build_directory branch from e0c7e25 to cc6f02b Compare December 19, 2018 16:36
It represents the path of RIOTBASE inside the docker container.
Prepare for when it can have a different value.
Update definition order for DOCKER_VOLUMES_AND_ENV.

* Localtime
* Mapping and env related to `RIOTBASE`
* Build directories
* Project
* CPU/BOARD/make
Return to which directory in the container this directory should be mapped.
Add functions to get volume and env arguments for a given directory environment
variable.

It handles:

 * variables with multiple directories like EXTERNAL_MODULE_DIRS
 * relative path
 * if the 'directories' variable is empty, it will not be exported to docker
Use RIOTPROJECT from within the riot repository if it is inside.

This means when it is the case to use:

 * Not mounting the directory to `riotproject`
 * Use `APPDIR` relative to inside RIOT

If it is not inside, do the same as before:

 * Mount the RIOTPROJECT to `riotproject`
 * Use `APPDIR` relative to RIOTPROJECT
Use BUILD_DIR from within the riot repository if it is inside.
Use the directories from in RIOT if possible for RIOTCPU/RIOTBOARD/RIOTMAKE.
@cladmi cladmi force-pushed the pr/make/docker/fix_relative_build_directory branch from cc6f02b to e03ad33 Compare February 28, 2019 13:47
@cladmi
Copy link
Contributor Author

cladmi commented Feb 28, 2019

I rebased on the current master and resolved conflicts. I will re-run all the tests again.

@cladmi
Copy link
Contributor Author

cladmi commented Feb 28, 2019

I could successfully re-run all the tests from

#9646 (comment)
and from
#9646 (comment)

With the same results.

When mounting directories for EXTERNAL_MODULE_DIRS it is not detected if they are in riotproject and so are still re-mounted in an external directory. It was explained in the test I originally described.

@cladmi cladmi added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Feb 28, 2019
They are remapped to `$(DOCKER_BUILD_ROOT)/external` if they are not
inside RIOT (usually the case but not for `tests/external_modul_dirs`).

If they are inside 'riotproject' they are currently also remapped to
'external'.

The value of `EXTERNAL_MODULE_DIRS` is then enforced by configuring it on
the command line as the application should not try to set it anymore.

The remapping is done in `external/directory_name` so cannot handle
multiple external directories with the same name.
@cladmi cladmi force-pushed the pr/make/docker/fix_relative_build_directory branch from e03ad33 to 3a17ddc Compare February 28, 2019 14:14
@MrKevinWeiss MrKevinWeiss self-requested a review March 7, 2019 14:04
@MrKevinWeiss
Copy link
Contributor

Sooo this seems ready... or is there something left to do?

@MrKevinWeiss
Copy link
Contributor

So within RIOT:

docker run --rm -t -u "$(id -u)" \
    -v '/usr/share/zoneinfo/Europe/Berlin:/etc/localtime:ro' -v '/home/kevinweiss/WorkingDirectory/RIOT:/data/riotbuild/riotbase' -e 'RIOTBASE=/data/riotbuild/riotbase' -e 'CCACHE_BASEDIR=/data/riotbuild/riotbase' -e 'BUILD_DIR=/data/riotbuild/riotbase/build' -e 'RIOTPROJECT=/data/riotbuild/riotbase' -e 'RIOTCPU=/data/riotbuild/riotbase/cpu' -e 'RIOTBOARD=/data/riotbuild/riotbase/boards' -e 'RIOTMAKE=/data/riotbuild/riotbase/makefiles'     \
     \
    -w '/data/riotbuild/riotbase/examples/hello-world/' \
    'riot/riotbuild:latest' make   

external:

docker run --rm -t -u "$(id -u)" \
    -v '/usr/share/zoneinfo/Europe/Berlin:/etc/localtime:ro' -v '/home/kevinweiss/WorkingDirectory/RIOT:/data/riotbuild/riotbase' -e 'RIOTBASE=/data/riotbuild/riotbase' -e 'CCACHE_BASEDIR=/data/riotbuild/riotbase' -e 'BUILD_DIR=/data/riotbuild/riotbase/build' -v '/home/kevinweiss/WorkingDirectory/hello-world:/data/riotbuild/riotproject' -e 'RIOTPROJECT=/data/riotbuild/riotproject' -e 'RIOTCPU=/data/riotbuild/riotbase/cpu' -e 'RIOTBOARD=/data/riotbuild/riotbase/boards' -e 'RIOTMAKE=/data/riotbuild/riotbase/makefiles'     \
     \
    -w '/data/riotbuild/riotproject/' \
    'riot/riotbuild:latest' make 

@MrKevinWeiss MrKevinWeiss merged commit e33e7bf into RIOT-OS:master Mar 8, 2019
@cladmi
Copy link
Contributor Author

cladmi commented Mar 11, 2019

Thank you for the review.

@cladmi cladmi deleted the pr/make/docker/fix_relative_build_directory branch March 11, 2019 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: build system Area: Build system Area: toolchain Area: toolchains; everything related to compilation, libc, linking, … CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants