conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.15k stars 971 forks source link

[question] how to know if building from the cache in a Conan 1.x/2.x compatible way #11563

Open datalogics-kam opened 2 years ago

datalogics-kam commented 2 years ago

I'm working on making one of our exemplar projects be as 2.x compatible as possible while still working in 1.x.

I want to prevent building/running the unit tests when in the local cache vs running them when building in a development/CI context.

Previously, the package used exports_sources and didn't export the test folders, i.e.:

    exports_sources = ('CMakeLists.txt', 'src/*', '!src/*/test/*', '!src/cmake-build-*/*', '!src/python/*',
                       '!src/test_common/*')

...and if the folders weren't there, the CMakeLists.txt just didn't build the tests. And the build() method said something like:

    if not self.in_local_cache:
        cmake.test()

But now I want to save the Git information and check out from the Git repository as necessary. In doing so, the tests are getting built, and I'd rather not have the time spent, and especially not to run them.

Is there going to be a way to know in Conan 2.x that the build is happening in response to a requirement instead of being at the root of the requirement graph? In other words, what can I use instead of in_local_cache?

datalogics-kam commented 2 years ago

I'm also looking for something I can pass into CMake like...

def generate(self):
    tc = CMakeToolchain()
    tc.variables['BUILD_TESTS'] = not self.in_local_cache   # What replaces this?
czoido commented 2 years ago

Hi @datalogics-kam,

I would suggest that you use different profiles in ci and locally and set the tools.build:skip_test configuration to skip the tests or not (you can also pass the conf via command line).

Hope this helps!

datalogics-kam commented 2 years ago

Hi, @czoido

I was starting to think something like that. The real situation is that I want to run the tests when the package is a consumer (which would be in development and CI). I see that _is_consumer variable sneaking around inside Conan 2.0.

Also, there's no point in building the tests either if building the package isn't going to run them.

I'm thinking of using an option enable_testing, default False, that we can set to True in local development and CI, and would default to False if the package was being built to satisfy a requirement, i.e., in the cache.

czoido commented 2 years ago

Hi @datalogics-kam, You can get the value of the conf and use it inside the recipes like:

def generate(self):
    tc = CMakeToolchain(self)
    tc.variables['BUILD_TESTS'] = not self.conf.get("tools.build:skip_test")

I don't recommend relying on the undocumented private _is_consumer variable, it's better to control that using the conf depending on the situation you are creating the package.

Hope this helps!

datalogics-kam commented 2 years ago

I won't rely on private variables like _is_consumer.

Using the conf variable still doesn't solve the problem, here's why. Let's say we're talking about skipping package A's tests.

czoido commented 2 years ago

Maybe you can set tools.build:skip_test=True in the global.conf file located in the Conan user home directory and just set skip_test=False for certain cases like the two first? Would that help?

datalogics-kam commented 2 years ago

Considering the third case, if I set skip_test=False, it will allow testing of B, but will also turn on testing of A again, because conf variables affect all packages.

I tried out the option idea, and it's a decent workaround for now. Options are per-package, so that lets us turn on testing for the consumer package. (We have a set of invoke tasks that kind of wrap around Conan and inject profiles and settings and options from a project yaml file, so that's easy to do)

datalogics-kam commented 2 years ago

This kind of brings up another question: Should there be a conf variable to skip tests when building a requirement in the cache?

memsharded commented 2 years ago

Hi @datalogics-kam

the idea with the new conf and the new approach facing 2.0 is that it is irrelevant if the package is built in the cache or in local user folder. We did a lot of work to make it work exactly the same in both places (like the new transparent integrations that allows conan install + cmake ... to do exactly the same as conan build, which was false in the past).

For this reason, we have removed the in_local_cache and develop attributes in 2.0. The idea is that everything in Conan 2.0 is nicely explicit. If you want to run the tests of a specific package, you can specify configuration also per package, like mypkg*:tools.build:skip_tests=False. Conan 2.0 has already implemented a generic package-name matching approach, already used in other places, allowing also to define a "consumer" pattern (&).

To summarize:

datalogics-kam commented 2 years ago

Hey, @memsharded

Thanks for the reply. The insights you give guide me to better use of Conan, including things like moving from options to conf for things that are build/test behaviors rather than the configuration of the package itself.

The develop and in_local_cache variables were, admittedly, really confusing, and I agree with their departure.

I had an opportunity to rethink how this is going. Bear in mind, that I'm thinking in terms our our company's needs, and not recipes I would contribute to Conan Center Index, in case that was a concern.

Instead of thinking of whether the package should behave differently if it's in the cache, I realized that what I really wanted was for the package to not run its tests if it has been exported. CCI recipes, for instance, don't configure and run tests for the packages they build; presumably the package was tested before it was released.

Same thing for us, for packages we export to our internal Conan repo.

What used to happen was that exports_sources didn't export the testing code, and the CMakeLists.txt didn't build the tests if the sources weren't there.

Since the package I'm working on now demonstrates using the SCM to get the sources[^0], of course the tests are there.

What I realized I could do was change the conan_data when exporting.

So now the export() looks like this:

    def export(self):
        git = Git(self, self.recipe_folder)
        scm_url, scm_commit = self._get_url_and_commit(git)
        # we store the current url and commit in conandata.yml
        update_conandata(self, {'sources': {'commit': scm_commit, 'url': scm_url},
                                self.name: {'build_tests': False}})

...and in generate(), there is:

        skip_test = self.conf.get('tools.build:skip_test', False)
        tc.variables['BUILD_TESTS'] = self.conan_data[self.name].get('build_tests', not skip_test)

(the conandata.yml in the Git repo doesn't have an entry for build_tests, so the user's conf preference will prevail there)

cmake.test() succeeds with a "No tests were found!!!" message if there aren't tests, so no conditional needed on that.

Thanks again for the input. I think I've found a reasonable resolution, so feel free to close this issue.

[^0]: This is on the way to making it so that one of our really large packages can build from an export. The source tree for the package is 813Mb, and we really didn't want to export that into a Conan recipe.