gradle-native
gradle-native copied to clipboard
Feature request: Option to disable the collapsing of single variants in task names
Related to GH 906, I'm working through my workaround described in GH 859, where I am able to target, for example, windows_x64_vs2013 and windows_x64_vs2015 in separate executions of Gradle by only configuring one platform and toolchain at a time, and I need the output paths for at minimum the final outputs and ideally all the intermediates to be different for each platform/toolchain combination.
At the moment, in the following code Gradle collapses variants for which only one is configured:
apply plugin: 'cpp'
model {
toolChains {
vs2013(VisualCpp) {
installDir = 'C:/Program Files (x86)/Microsoft Visual Studio 12.0'
}
}
components {
TestProject(NativeLibrarySpec) {
targetPlatform 'windows_x86-64'
}
}
}
So my task names look like 'TestProjectSharedLibrary' and have outputs like:
build
├───libs
│ └───testProject
│ ├───shared
│ └───static
├───objs
│ └───testProject
│ ├───shared
│ │ └───TestProjectCpp
│ │ └───c6j6y5w4zqfxu2hrtsy45883i
│ └───static
│ └───TestProjectCpp
│ └───c6j6y5w4zqfxu2hrtsy45883i
└───tmp
├───compileTestProjectSharedLibraryTestProjectCpp
├───compileTestProjectStaticLibraryTestProjectCpp
├───createTestProjectStaticLibrary
└───linkTestProjectSharedLibrary
Whereas if I target a second platform that I know I can't build, for example I'm on Windows, so:
TestProject(NativeLibrarySpec) {
targetPlatform 'windows_x86-64'
targetPlatform 'unix_x86'
}
I will have outputs like:
build
├───libs
│ └───testProject
│ ├───shared
│ │ └───windows_x86-64
│ └───static
│ └───windows_x86-64
├───objs
│ └───testProject
│ ├───shared
│ │ └───windows_x86-64
│ │ └───TestProjectCpp
│ │ └───c6j6y5w4zqfxu2hrtsy45883i
│ └───static
│ └───windows_x86-64
│ └───TestProjectCpp
│ └───c6j6y5w4zqfxu2hrtsy45883i
└───tmp
├───compileTestProjectWindows_x86-64SharedLibraryTestProjectCpp
├───compileTestProjectWindows_x86-64StaticLibraryTestProjectCpp
├───createTestProjectWindows_x86-64StaticLibrary
└───linkTestProjectWindows_x86-64SharedLibrary
Unfortunately, in my current use case (see linked issues for problems targeting VS2013 and VS2015 simultaneously), I have to target a second platform that I either know Gradle cannot build in the current environment or do not expect Gradle to build in order to have the targetPlatform name in my output paths, which leads to confusion for the users in terms of available targets and an unearthly number of tasks to try to hide or disable.
It would be nice to have the option to prevent Gradle collapsing the variants (assume all variants for simplicity, e.g. Flavor, BuildType, Platform?) when only one is configured.
Steps to Reproduce (for bugs)
Source code: collapsedVariants-example.zip
Your Environment
Windows 10 x64, Gradle 3.2, MSVC 2013
Thanks @philmcardle for the well-constructed issue. You are totally right this is a strange behavior to have. The basic idea is to scale the complexity of the path with the complexity of the configuration.
One way I worked around this behavior in the past was to pass around NativeBinarySpec
instance. I would get a binary reference through the model which is static in its shape and filter the various collection with find
, findAll
, etc. Then, when I actually need the path, I would simply get the output with the binary's property: getSharedLibaryFile()
, getStaticLibraryFile()
, etc. This effectively eliminated almost all path reconstruction we had to do. This pattern has the advantage of keeping the full context wherever you need the binary.
It's kind of like handling files in Java. You use an instance of the File
class and only when you need the path that you convert it into a String
through File.getPath()
.
If the underlying problem is related to consuming those binary files from another automation script, I would suggest to either:
- Create a receipt files with all the required path, or
- Build a package (folder structure) with the required files
The other system would simply consume a consistent structure that you have control over. Note the on-disk layout of the build folder for native has changed in the past and could change in the future. Eliminating dependency on the layout is a sure way to ensure forward compatibility.
That been said could you comment on any user case/scenario that can't be cover by the work around exposed here? That would greatly help use improving Gradle by introducing the required public API or features.
So my original use case for this was as a result of my original workaround for #859. Because I had only one toolChain
defined at a time, and in my case had only one targetPlatform
at a time, my configuration complexity was limited, and my build outputs were written to a directory structure like that seen in tree 1, above.
Consequently, when I used a custom parameter to have Gradle to configure and target the VS2013 or VS2015 toolChain
in different build runs, my build outputs from both builds were written to the same directory tree in both cases and the workarounds I had in gradle/gradle#906 (1. setStaticLibraryFile
/ setSharedLibraryFile
/ setSharedLibraryLinkFile
- although there were no equivalents for NativeExecutableBinarySpec or GoogleTestTestSuiteBinarySpec; 2. modifying the paths on the link / install / create tasks) were incomplete and unwieldy (I couldn't change the tmp directory, for instance).
In that scenario, while Gradle elegantly handled noticing that the intermediate and outputs were wrong, it left a developer with no ability to iterate on both tool chains at the same time without rebuilding from scratch each time, further, I couldn't easily keep the built outputs for both versions of VS in the same workspace for use in any other script and I had quite a few outright hacks in play that I wasn't comfortable with (also, if I had built tasks into Gradle to deploy the outputs, it would have to use all the same hacks, and wouldn't be able to intelligently build the per-toolchain/platform dependencies for a deploy task). I appreciate not everyone needs to target multiple versions of VS at the same time, but as we're writing software to be used by other developers then it's a need for us 🙂
If I implement this solution from gradle/gradle-native#1039, I'm in a much better position in this respect however it would be appreciated to have the option as described to just have the full complexity paths, but maybe I'm not thinking in a sufficiently Gradle way 🙂
I don't fully know what I'm going to need yet as we aren't yet very far into this project. Both of your listed workarounds are good too.
It is likely that in the absence of more control over the output paths that I may ultimately do something like https://discuss.gradle.org/t/native-how-to-reference-the-buildtypes-value-in-a-component/20256/4 or https://discuss.gradle.org/t/add-suffix-relative-to-buildtypes-or-platforms-for-native-builds/19999/4, but I don't know for sure yet.
There's a lot of advantages to being able to predict where something will be, on the off-chance that I can't run everything within Gradle, or when instructing developers on the project.
Just thinking out loud now.
Thanks Phil for exposing your use case. Sound like opening BinaryNamingScheme
to the public API could help with your scenario. You can still use it now but I must warn that it's highly discouraged to use Gradle internal API as it may limit your ability to migrate to newer version of Gradle. For completeness and as another workaround until this issue is addressed, your could consider BinarySpecInternal.getBinaryNamingScheme()
.
Don't hesitate to share more use case on this to help use choose the right path forward.