Native: Trying to constrain which platforms a toolchain supports

Hi,

In order to effectively build for multiple different versions of Visual Studio at once (and a lot of other things hereafter), I need to be able to tell Gradle that VS2013 and VS2015 (for example) are different targets.

I won’t go into the details here, but I have separately managed to coerce Gradle to only build for specific toolchains using command-line arguments and conditional configuration of toolchains, so I can already implement something like this work in a way that allows me to build for one target at a time, but that feels like artificially limiting Gradle, and I’d like for my users to be able to target any toolchain they have installed on their machine without having to run Gradle once for each.

I’ve considered trying to implement the VS2013/VS2015 separation using either flavors or an extension property on the platform, but the latter seems the most appropriate place for it, and it’s my best guess for where it will be when Gradle supports it itself.

I’m not necessarily looking for a long-term solution, just something that isn’t horrible, as I expect Gradle to support this eventually.

At the moment, I have the following code, reduced to the minimum from a much larger project:

apply plugin: 'cpp'

model {

    platforms {
        windows_x64_vs2015 {
            architecture 'x86_64'
            operatingSystem 'windows'
            ext.toolChain = 'vs2015'
        }
        windows_x64_vs2013 {
            architecture 'x86_64'
            operatingSystem 'windows'
            ext.toolChain = 'vs2013'
        }            
    }       
    
    toolChains  {

        vs2015(VisualCpp) {
            installDir = 'C:/Program Files (x86)/Microsoft Visual Studio 14.0'
        }

        vs2013(VisualCpp) {
            installDir = 'C:/Program Files (x86)/Microsoft Visual Studio 12.0'
        }

    }

    components {

        TestProject(NativeLibrarySpec) {
            targetPlatform 'windows_x64_vs2013'
            targetPlatform 'windows_x64_vs2015'

            binaries {

                all {
                
                    if (toolChain in VisualCpp) {
        
                        if (toolChain.name == 'vs2015') {
                            // UCRT path is a hack for the UCRT/VS2015 support missing from Gradle (https://github.com/gradle/gradle/pull/704)
                            cppCompiler.args '/IC:/Program Files (x86)/Windows Kits/10/Include/10.0.10240.0/ucrt'
                            linker.args '/LIBPATH:C:/Program Files (x86)/Windows Kits/10/Lib/10.0.10240.0/ucrt/x64'                            
                        }

                    }
                }
            }

            binaries {
                all { binary ->
                    println "* Building binary ${binary.name} with toolChain ${binary.toolChain.name}"
                }
            }
        }
    }

}

Obviously, at the moment, because I have VS2015 installed, VS2015 builds both of these targets.

* Building binary windows_x64_vs2013SharedLibrary with toolChain vs2015
* Building binary windows_x64_vs2013StaticLibrary with toolChain vs2015
* Building binary windows_x64_vs2015SharedLibrary with toolChain vs2015
* Building binary windows_x64_vs2015StaticLibrary with toolChain vs2015
:compileTestProjectWindows_x64_vs2013SharedLibraryTestProjectCpp UP-TO-DATE
:linkTestProjectWindows_x64_vs2013SharedLibrary UP-TO-DATE
:TestProjectWindows_x64_vs2013SharedLibrary UP-TO-DATE
:compileTestProjectWindows_x64_vs2013StaticLibraryTestProjectCpp UP-TO-DATE
:createTestProjectWindows_x64_vs2013StaticLibrary UP-TO-DATE
:TestProjectWindows_x64_vs2013StaticLibrary UP-TO-DATE
:compileTestProjectWindows_x64_vs2015SharedLibraryTestProjectCpp UP-TO-DATE
:linkTestProjectWindows_x64_vs2015SharedLibrary UP-TO-DATE
:TestProjectWindows_x64_vs2015SharedLibrary UP-TO-DATE
:compileTestProjectWindows_x64_vs2015StaticLibraryTestProjectCpp UP-TO-DATE
:createTestProjectWindows_x64_vs2015StaticLibrary UP-TO-DATE
:TestProjectWindows_x64_vs2015StaticLibrary UP-TO-DATE
:assemble UP-TO-DATE

I believe that my problem code is provisionally this: https://github.com/gradle/gradle/blob/master/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppInstall.java#L53

public boolean isSupportedPlatform(NativePlatformInternal targetPlatform) {
    return targetPlatform.getOperatingSystem().isWindows()
            && (architectureDescriptors.containsKey(getPlatformArchitecture(targetPlatform)));
}

I was hoping I can get access to that in a RuleSource plugin, but truthfully I have no idea.

Despite my best efforts to read the available samples and documentation, I’m not completely sure what RuleSource plugins can actually do, or how, or what control you can exert over the existing Gradle code.

I’ve also considered whether code like this https://github.com/gradle/gradle/blob/master/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/plugins/MicrosoftVisualCppCompilerPlugin.java#L51 would afford me the possibility of better configuring the toolchain, but I’m not sure how viable the use of that is either.

I can see that the GccCompatibleToolChain has support for things like targets, and a superior GccCommandLineToolConfiguration versus the CommandLineToolConfiguration available to VisualCpp, both of which other posts suggest I might be able to use to coerce something similar to this, although, again, I’m not sure, and I haven’t tried to any great extent as gcc isn’t a problem case.

I would greatly appreciate any pointers as to where to start, or how else to solve this problem.

If I can’t find an even moderately clean solution, I’ll just resort to targeting one toolchain per Gradle run using my own command-line work and replace it when Gradle captures this. In truth, the average developer only works to one target platform at a time, so it’s not a deal-breaker, but it’s something that should be easier with Gradle.

Many thanks,

Phil

Reported feature request https://github.com/gradle/gradle/issues/859 as a follow-up.

Phil