Native: Excluding source depending on toolchain/Platform/Architecture/OperatingSystem

Hi

I am looking a simple way to exclude sources from a sourceset like

components {      
    Foundation(NativeLibrarySpec) {
        sources {
            cpp {
                source {
                    srcDir 'src'
                    include '**/*.cpp'
                    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
                        exclude 'SyslogChannel.cpp'
                        exclude 'OpcomChannel.cpp'
                        exclude 'AndroidLogChannel.cpp'
                    }

This makes gradle unhappy. Can this kind of conditional include/exclude be added to the Native plugin?

I may be mistaken, but from my current understanding of how Gradle works with source sets, you can’t change a source set for different targets (platforms, build types, flavors, etc.). AFAIK, this is because a source set is just a collection of files that is then shared between the different output binaries. Binaries are generated for all the different platform, build type and flavor combinations.

There are some ways to work around this. The easiest is probably to have compile-time defines to indicate the target platform. The build.gradle file then contains something like:

components {
    Foundation(NativeLibrarySpec) {
        sources {
            cpp {
                source {
                    srcDir 'src'
                    include '**/*.cpp'
                }
            }
        }
        binaries.all {
            if (Os.isFamily(Os.FAMILY_WINDOWS))
                cppCompiler.define '__OS_WINDOWS__' // Or any other custom definition
        }
    }
}

This is what we’re using now (see here), but IMHO there should be another more elegant way.

Components do have target variations, so an option would be to create platform specific components. This should work normally, but there’s the burden that you’re manually taking care of the component variants, which might not be an option (especially for native libraries).

Maybe there’s a simpler way, but this is what I’ve found so far.

You can declare SourceSets per variant (platforms, build types, flavors etc.) of a component.

E.g.:

apply plugin: 'cpp'
model {
    flavors {
        unix
        windows
    }
    components {
        Foundation(NativeLibrarySpec) {
            sources {
                cpp {
                    source.srcDir 'src'
                }
            }
            binaries.all {
                if(flavor in flavors.unix) {
                    sources {
                        cppGcc(CppSourceSet) {
                            source.srcDir 'src-gcc'
                        }
                    }
                }
            }
        }
    }
}

This way you can have a sourceset that contains your common code, and sourcesets that contain variant specific code depending on target platforms, toolchains, build types, flavors etc…

See the Native Binary Variants section of the user guide.

HTH

3 Likes

This is very interesting. So binaries have their own source sets? This makes things easier :slight_smile:

Might be interesting to add this information to the user guide (maybe just a sentence?).

This is very interesting. So binaries have their own source sets? This makes things easier :slight_smile:
Might be interesting to add this information to the user guide (maybe just a sentence?).

@jvff The information that NativeBinarySpecs can have sources is present in the javadoc. That’s not much I agree. Any chance you consider contributing a PR with a few words?

While your proposal is quite interesting, it does not solve my issue because I cannot change the organization of the Cpp src directory. All sources, be for Linux or Windows or WindowsCE are all together in this unique src directory.

So from my perspective, a solution with a conditional include/exclude is more adapted.

@zosrothko I just tried your example with a conditional and it works for me with simple cpp sources. What exactly is the error you get?

With
project(“:Foundation”) {
model {
components {
PocoFoundation(NativeLibrarySpec) {
sources {
mc {
source {
srcDir ‘src’
include ‘/*.mc’
}
}
rc {
source {
srcDir ‘src’
include '
/.rc’
}
}
c {
source {
srcDir ‘src’
include '**/
.c’
}
exportedHeaders {
srcDir ‘include’
}
}
cpp {
source {
srcDir ‘src’
include ‘**/.cpp’
exclude 'Environment_
.cpp’
exclude ‘FPEnvironment_.cpp’
exclude 'Timezone_
.cpp’
exclude ‘DirectoryIterator_.cpp’
exclude 'File_
.cpp’
exclude ‘FileStream_.cpp’
exclude 'Path_
.cpp’
exclude ‘LogFile_.cpp’
exclude 'NamedEvent_
.cpp’
exclude ‘NamedMutex_.cpp’
exclude 'PipeImpl_
.cpp’
exclude ‘Process_.cpp’
exclude 'SharedMemory_
.cpp’
exclude ‘SharedLibrary_.cpp’
exclude 'Event_
.cpp’
exclude ‘Mutex_.cpp’
exclude 'RWLock_
.cpp’
exclude ‘Semaphore_.cpp’
exclude 'Thread_
.cpp’
if (toolChain in VisualCpp) {
exclude ‘SyslogChannel.cpp’
exclude ‘OpcomChannel.cpp’
exclude ‘AndroidLogChannel.cpp’
}
}
exportedHeaders {
srcDir ‘include’
}
}
}
}
}
binaries {
all {
if (toolChain in VisualCpp) {
linker.args ‘ws2_32.lib’
linker.args ‘iphlpapi.lib’
}
}
withType(SharedLibraryBinarySpec) {
if (toolChain in VisualCpp) {
cCompiler.define “Foundation_EXPORTS”
cppCompiler.define “Foundation_EXPORTS”
}
}
}
}

I got

  • What went wrong:
    A problem occurred configuring project ‘:Foundation’.

Exception thrown while executing model rule: model.components > create(PocoFoundation) > named(cpp)
No such property: toolChain for class: org.gradle.api.internal.file.DefaultSourceDirectorySet

First attempt at contributing to the docs: https://github.com/gradle/gradle/pull/703

1 Like

Yes, the conditional is not the issue here, toolChain is a property of binaries.

In your original attempt you used the following conditional:

if (Os.isFamily(Os.FAMILY_WINDOWS)) {
  // Exclude some files if the build is running on Windows
  exclude '...'
}

What exactly was not working for you with this approach?

I got lost by the various kind of tests that can be done. Below code is working as you mention

    model {
        def os = org.gradle.internal.os.OperatingSystem.current()
        components {
            PocoFoundation(NativeLibrarySpec) {
                sources {
                    cpp {
                        source {
                            srcDir 'src'
                            include '**/*.cpp'
                            if (os.isWindows()) {
                                exclude 'SyslogChannel.cpp'
                                exclude 'OpcomChannel.cpp'
                                exclude 'AndroidLogChannel.cpp'
                            }

Thank for your help, it works!