Closed zosrothko closed 5 years ago
At the moment, we don't allow mixing cpp-library
and cpp-application
into the same project to create multi-components project like in the software model. The reason being it goes against the pattern expressed everywhere else in Gradle. Multiple components in a project will be expressed in a more focused way like JNI project or multiple test suite per project.
For your specific scenario, you could model the structure of the project as a setting plugin that simplifies the project creation. Given the pattern used throughout the Poco organization is pretty clear, you should be able to model it so that you can use a custom DSL that does the right configuration to keep the organization of the Gradle logic concise. For example, you could have the following settings.gradle
. Keep in mind the code could be moved to buildSrc
and apply the plugin.
import javax.inject.Inject
class PocoLibraryProject {
private final String path
final List<String> samples = []
@Inject
PocoLibraryProject(String path) {
this.path = path
}
void sample(String name) {
samples.add(name)
}
}
class PocoLayoutExtension {
private final Settings settings
private final ObjectFactory objectFactory
@Inject
PocoLayoutExtension(ObjectFactory objectFactory, Settings settings) {
this.objectFactory = objectFactory
this.settings = settings
}
void library(String path, Action<? extends PocoLibraryProject> action = null) {
settings.include(path)
if (action != null) {
def spec = objectFactory.newInstance(PocoLibraryProject, path)
action.execute(spec)
for (String it : spec.samples) {
String projectPath = "${path}:${it}"
settings.include(projectPath)
settings.project(":${projectPath}").projectDir = new File(settings.rootDir, path.replace(":", "/") + "/samples/${it}")
settings.gradle.rootProject { proj ->
proj.project(":${projectPath}") {
// TODO: Configure convention for the sample such as:
// - applying `cpp-application`
// - adding a dependency to the library
}
}
}
// TODO: Handle the test case in a similar way
}
}
}
class PocoProjectLayoutPlugin implements Plugin<Settings> {
private final ObjectFactory objectFactory
@Inject
PocoProjectLayoutPlugin(ObjectFactory objectFactory) {
this.objectFactory = objectFactory
}
void apply(Settings settings) {
def subprojects = objectFactory.newInstance(PocoLayoutExtension, settings)
settings.extensions.add("subprojects", subprojects)
}
}
apply plugin: PocoProjectLayoutPlugin
subprojects {
library("Foundation") {
sample("ActiveMethod")
sample("Activity")
//...
}
library("SQL") {
sample("Binding")
sample("RecordSet")
//...
}
library("SQL:MySQL")
}
Let favour https://github.com/gradle/gradle-native/issues/988 for now and reopen when we get a clearer need for this scenario.
Note that you can still configure all the sample and testsuite from a single build.gradle
file by doing something like:
project("ActiveMethod") {
// ...
}
project("Activity") {
// ...
}
You could also keep the subproject samples
under each library by avoiding the redirection of the projectDir
inside samples
so that a sample would have the following path :Foundation:samples:ActiveMethod
.
My bad refer to https://github.com/gradle/gradle-native/issues/216.
@lacasseio From my perspective, the current limitation that forbids the mix of cpp-library/cpp-application plugins is quite ennoying. Ideally I would like to something like the root project script as below
subprojects {
apply plugin: 'cpp-library'
apply plugin: 'cpp-application'
apply plugin: 'cpp-unit-test'
library {
def toolChain = toolChain.getOrNull()
task.WhenType(CppCompile) {
if (toolChain instanceof VisualCpp) {
compilerArgs = ["/EHsc" /* additional global compiler options go there */]
}
}
}
application {
def toolChain = toolChain.getOrNull()
task.WhenType(CppCompile) {
if (toolChain instanceof VisualCpp) {
compilerArgs = ["/EHsc" /* additional global compiler options go there */]
}
}
}
unitTest {
def toolChain = toolChain.getOrNull()
task.WhenType(CppCompile) {
if (toolChain instanceof VisualCpp) {
compilerArgs = ["/EHsc" /* additional global compiler options go there */]
}
}
}
}
Poco has 19 submodules, each one beeing a cpp-library with a testsuite as a cpp-application. Each submodule has a link dimension [static, semistatic, shared], a mode dimension [debug, release] and an architecture dimension [x86, adm64]. Moreover 15 of all submodules have also a set of samples which are cpp-application. There are about 64 samples to build refering the matrix of the submodules above.
The number of the if/else
choice for setting the proper compiler options and linker options for the matrix above is 3x2x2 = 12. If there is no way to have a global setting inherited by each cpp-application/cpp-library/unitTest as does the Software Model, one would have to make (19 x 12) * 2 + 64 x 12 = 996 editions to 102 gradle scripts. That's not a workable solution.
The build scripts in Gradle are meant to configure the model for a particular project. How you model the project is a different issue. In the case of Poco, the pattern is really clear. It would be recommended to model the pattern which would allow each library to be treated as it's own meta-project which includes all the subproject. To this end, the meta-project would push the configuration on 1 or several sub-project depending on what is the right thing to do.
Given those meta-projects are highly opinionated for each organization, we are trying to focus on ways to simplify the modelling as opposed to make the low-level API more flexible (aka allowing declaring multiple components). On itself, the cpp-application
and cpp-library
are kind of like meta-component. They configure multiple components which are invisible to the user. We may allow users to create meta-component inside a project but the functional plugin app-application
and cpp-library
are not meant to be mixed together within the same project. In Java, more "components" are created via the sourceSets
DSL. Similar patterns may be implemented in native but not to the extent of the software model.
The solution at this stage is to create those meta-projects what would configure multiple projects at the same time making it possible to only configure a handful of build scripts. Having to do 996 editions for a change of flags or anything with that regards is a problem with the modelling of the current pattern.
From my perspective, each Poco library (Foundation, Crypto, Net, JSON, etc) should only have a single build script applying a plugin like org.pocoproject.poco-library
that configure each subproject. You can have a DSL the way that makes the most sense for your project like:
apply plugin: 'org.pocoproject.poco-library'
library {
// configuration for the library
}
testSuites.all {
compilerArgs.set([...])
}
samples.all {
// something else here
}
The plugin can configure lifecycle tasks that hook into the subprojects so you can do something like:
./gradlew :Foundation:check
or ./gradlew :Foundation:prepareSampleZip
Gradle should allow you to easily build those modelling.
When building a multi modules project, that contains both cpp library and cpp-application, one needs to specify global options/parameters/tasks for some or all submodules. Below is the organisation of a the Poco Foundation module
Expected Behavior
A working build as with the Rule based plugins
Current Behavior
Build file 'Z:\git\gradle-bug\build.gradle' line: 2
Context
Cannot use the new cpp-plugins
Steps to Reproduce (for bugs)
build the following build.gradle script
Your Environment
Gradle 5.1.1
Build time: 2019-01-10 23:05:02 UTC Revision: 3c9abb645fb83932c44e8610642393ad62116807
Kotlin DSL: 1.1.1 Kotlin: 1.3.11 Groovy: 2.5.4 Ant: Apache Ant(TM) version 1.9.13 compiled on July 10 2018 JVM: 1.8.0_181 (Oracle Corporation 25.181-b13) OS: Windows 10 10.0 amd64