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
Feature/environment propagate #8534
Feature/environment propagate #8534
Conversation
c26d153
to
75ff512
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The overall idea looks pretty good. I miss some more unit tests mostly and we need to try this with more pieces (toolchains etc) and real recipes to confirm that it fits.
|
||
def test_profile(): | ||
myprofile = textwrap.dedent(""" | ||
# define |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to specify a separator in the profile?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it seems the syntax will be weird, especially to designate whitespaces
@memsharded sublime. Love it. |
Excellent work! I really now appreciate the cleaner separation and management between the environment variables for build and run. I think the I will use this opportunity to point out that the
I understand it's a bit too ambitious and un-proven as a concept internally yet, so it's just a suggestion for future. But, I'm confident it's a great idea to provide this option. I definitely think it would be a better internal implementation for our own python code such as this (and every other future build system wrapper). But also, consider the vast number of other users trying to orchestrate Conan and these environments with their own python code, or cmake scripts, etc. It's really unnecessary and has significant disadvantages to have to go through the shell scripts with to get to the environment variables like this: def environment_wrap_command(filename, cmd):
if filename.endswith(".bat"):
return "{} && {}".format(filename, cmd)
elif filename.endswith(".sh"):
return 'bash -c ". {} && {}"'.format(filename, cmd.replace('"', r'\"'))
raise Exception("Unsupported environment file type {}".format(filename)) The fact that we're not even providing any option to produce generic That was the only thing I would suggest for fundamental change, perhaps in the next iteration. Even without that, this is a huge improvement and feels like we're closer to having this part really mastered. |
Thanks @solvingj for the review. There are a few things that prevent to have a nice solution based on generic
Note that the shell/batch script is still mandatory, we cannot go without it. After a Said that, I think that generating serialized xxxx.env files from the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to understand better (I always confuse build vs host, to me it's host vs target 😊):
- build requirement has
bin_dirs
/lib_dirs
(e.g. a tool cmake, flatc) -> ends up inbuildenv
(PATH
/LD_LIBRARY_PATH
) - package has
bin_dirs
/lib_dirs
and it's application -> ends up inrunenv
when I doconan install <ref> -f VirtualEnv
- Package is a (host) requirement and has
bin_dirs
/lib_dirs
-> ends up inrunenv
.
correct? So for development I activate only buildenv
and get my tools.
But then buildenv_info
/ runenv_info
are less clear:
- a package is build requirement: where do its
build
/run
infos go? - a package is a (host) requirement: ditto, where do explicit
build
/run
envs end up?
Now the most unclear thing is env_info.PYTHONPATH
. We use python packages and they can be both build requirement (as a plug in for build tool) and ordinary (a test runner when package is consumed).
Naturally I would expect to use buildenv
in first case and runenv
in the second. What are the needed changes for my setup? Should those recipes be changed to set both build_env_info
and run_env
_info properties?
Another thing to confirm: requirements from profile are always build ones, right? Asking because we have Conan packages with gcc and python and they're part of profile. However when running tests I would like to have them: python for test runner and gcov to process coverage (thus python should be in PATH
) so I have to activate both build and run environments, right?
The last part is a bit tricky with lockfiles: I can't specify profile when using lockfile, so I have to create a simple consumer file with single dependency. Then (as in item 3) my bin_dirs
/ lib_dirs
should end up in runenv
, right?
conan/tools/env/environment.py
Outdated
pass | ||
|
||
|
||
def environment_wrap_command(filename, cmd): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be made somehow extensible (even maybe a jinja template)? bash may not be present everywhere (well, maybe sh
is required for POSIX).
What about powershell on Windows?
Also for linux sh
will do for running from toolchain, but support for other shells should be possible for user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that the current scripts are using bash syntax, it is not just calling it, the scripts themselves should be adapted for other shells. If it is possible to make the xxxenv.sh scripts shell generic, that would be great.
The important bit I needed specific bash features if for capturing the environment for the deactivate_xxx.sh
script.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bash requirement has been removed. Powershell support will also be added.
conan/tools/env/environment.py
Outdated
if filename.endswith(".bat"): | ||
return "{} && {}".format(filename, cmd) | ||
elif filename.endswith(".sh"): | ||
return 'bash -c ". {} && {}"'.format(filename, cmd.replace('"', r'\"')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like an escaping hell (backslashes need to be escaped as well for example, maybe spaces...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our tests run by default with spaces in paths, but indeed we should check it. We are adding more real tests that will hopefully cover these cases.
conan/tools/env/environment.py
Outdated
self._values[name] = value + [_PathSep] + [_EnvVarPlaceHolder] | ||
|
||
def save_bat(self, filename, generate_deactivate=True, pathsep=os.pathsep): | ||
deactivate = textwrap.dedent("""\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another point about Jinja: if you have those templates then you can just go through the list of templates and generate each one. This will be customizeable and easily extensible. For example add setting:
virtualenv.templates=buildenv.sh.j2,runenv.sh.j2,buildenv.fish.j2
(Also modules / blocks can be loaded from config file location).
I rewrote my generator in Jinja. The template is arguably easier to read but python code is significantly simpler without all if'ed logic.
Another benefit of such user-extensibility is that it would be easier to write plain .env
files as @solvingj asked and which could be used in IDE (at least many VS Code plugins start supporting .env
files: python, C++ debugger).
Also doing this as a template doesn't mean immediate decision to publish as a user-facing contract, but still (I believe) will reduce complexity of python code.
The only trick is to carefully handle this for pyinstaller / zip-friendlyness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree it would be great to provide templates for this, and provide user extensibility. At this point my priority is to define the new paradigm:
- How generators access dependencies information via a visitor (ConanFileDependencies)
- The new separation of build and run envs
- The explicit generation of environment scripts for handling env-vars instead of doing everything dynamically in Conan (and thus not allowing many user flows)
- A clear and explicit mechanism to define env-var operations: append, prepend, clear, define, and how these compose for multiple levels (profile, build-requires, toolchains...)
Still the problem of .env files is not fully clear. In Conan we clearly have the user demand for those different operations: append, prepend, define, unset, and those .env files cannot do that. Also, in Conan users might need to concatenate more than one environment definition (for example the definition coming from profiles and build-requires, and another definition coming from a toolchain), that both will need to perform different operations like appending. Those .env files cannot do this either, up to my knowledge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still the problem of .env files is not fully clear.
At least in VS Code many plugins can use .env
files instead of having settings. For example, python extension appends PYTHONPATH
from it to its internal search path, C++ extension has similar option in debug configuration (for example to run program with modified LD_LIBRARY_PATH
).
It may not even be possible to run IDE in such an activated environment, because, for example for workspace each folder (repo / project) has own settings / environment.
I'm not suggesting adding this by default, but some users may find it useful to customize and produce own files, even if they for example capture complete variables at the install time.
The mapping of explicitly define env-vars in
A build requirement should only define
|
Yes, I guess so. Maybe if the tests are only intended to run in the
The profiles will also contain
This is not very clear, lockfiles contain a copy of the profiles, shouldn't require any specific setup? |
I am experimenting with removing the environment capture, and it works without bash, which I agree would be better. I'll check if there is a possibility to capture the "deactivate_xxx.sh" script in pure shell without using bash, and at the moment, I am making the generation of "deactivate_xxxx.xxx" scripts opt-in, instead of opt-out. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe if the tests are only intended to run in the build() method, it could be considered a "buildenv" only.
We run tests separately. So job1 builds conan package, uploads it and publishes a lockfile as an artifact. Next jobs take this artifact, do echo -e "[requires]\n$REF" > conanfile.txt && conan install . -g virtualenv -g virtualrunenv -o pkgname:testing=True
and then like python3 test.py
which is in the path and package-specific so it's the purpose of Conan package to provide right info to run particular tests.
However this is slightly irrelevant: PYTHONPATH
is defined explicitly and I can put it to both run_env and build_env. This part is straight-forward.
I think my primary question was about autorun_environment
(new function in this PR) and where do PATH
and library paths end up for requirements and build requirements (including ones coming from profiles).
conan/tools/env/environment.py
Outdated
self._values[name] = value + [_PathSep] + [_EnvVarPlaceHolder] | ||
|
||
def save_bat(self, filename, generate_deactivate=True, pathsep=os.pathsep): | ||
deactivate = textwrap.dedent("""\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still the problem of .env files is not fully clear.
At least in VS Code many plugins can use .env
files instead of having settings. For example, python extension appends PYTHONPATH
from it to its internal search path, C++ extension has similar option in debug configuration (for example to run program with modified LD_LIBRARY_PATH
).
It may not even be possible to run IDE in such an activated environment, because, for example for workspace each folder (repo / project) has own settings / environment.
I'm not suggesting adding this by default, but some users may find it useful to customize and produce own files, even if they for example capture complete variables at the install time.
For me is very important that everyone understand that we are building now the foundations of the Conan 2.0 and only a complete model will allow us to build on top of it the N layers to complete a successful Conan 2.0. So my vote is a big YES to merge this. |
No, it doesn't represent the reality better. It is not about UX. It is about the model. It is adding an artificial environment that is not needed at all (we are waiting for an example, we have +750 recipes in ConanCenter and no one of them requires it). Without a minimal example, it is as arbitrary as adding |
Multiple examples of variables that should exist at
"Not needed" is different from "useless". Can the recipes work without this feature, sure! they work! (even with vcpkg :P ) Can these variables exist at build time? sure, they can! but they shouldn't. That's what I mean by a better model. |
All those variables are needed when running as a build-requires and when running as standalone application. Why do you think you don't need those variables when using those packages as build-requires? |
I don't think that, a better model that doesn't mix things up allows to manage it nicely: |
@lasote do you talk about the need of something like def package_info(self):
self.buildenv_info.FOO = "Bar"
self.runenv_info.HELLO = "World" or something other? Because setting a Setting a single |
@jgsogo to be honest, I don't think
but at the same time I may run ARM (host) binaries:
I need
I don't see how it helps, it only complicates the things, introducing additional challenges in UX.
IMO this is too much and super confusing. |
There are recipes that declare 2 different types of environment variables, and there are examples of both usages in ConanCenter. I am not even talking about build-requires, just a regular recipe containing a library as regular requires, in the “host” context: Environment variables for build-time, most common example is XXX_ROOT self.env_info.MYLIB_ROOT = self.package_folder.replace("\\", "/") This one is an environment variable for building against it, more specifically for CMake find_package functionality to find it: https://cmake.org/cmake/help/latest/envvar/PackageName_ROOT.html. There are a few packages in ConanCenter using this approach. This variable must exist in the On the other hand, there are env-vars for runtime, typically something like: self.env_info.MYLIB_DATADIR = os.path.join(bindir, "datadir") This is also something that can be found in ConanCenter recipes. This is the location needed at application runtime, to locate the necessary resources to run applications linking with this library. This information must exist in the The necessary representation for this is something like: self.buildenv_info.MYLIB_ROOT = self.package_folder.replace("\\", "/")
self.runenv_info.MYLIB_DATADIR = os.path.join(bindir, "datadir") Otherwise it will be impossible to distinguish the right target environment script for each one. |
Seeing all the info, I have decided to move the PR forward as-is, there are a few pieces here that we need to keep moving forward. Even if it adds extra-model, it seems to exist real use cases that requires this flexibility, so I am keeping it. This part will not be documented at this moment, and we will keep evolving and investigating what is necessary and what not as we evolve the graph model. If we realize later that a single definition of environment is good enough, we will revert it. Thanks all for the feedback! |
Is it possible to keep all 3 instead of manual combination? |
Thanks for the example. Now I understand the motivation after that proposal. This is something people have been doing with We can change our minds and start recommending this approach with environment variables, no problem. 👍 Questions in order to help users in future issues:
And then, there are lots of details, but for me at the moment, these are some important points that will help me to make up my mind. Thanks |
Changelog: Feature: New environment management, with correct value capturing and restoring on deactivate
Docs: conan-io/docs#2060
New approach to environment (env-vars) management:
VirtualEnv
buildenv.xxx
andrunenv.xxx
cpp_info
definition: exes, bin-paths, lib_paths to define PATH, LD_LIBRARY_PATH, DYLD_LIBRARY_PATH envvarsConanfileDependencies
CMakeGen