Custom plugin to customize C++ plugin

I’m looking for a way to create my own version of a C++ project.
The plan is to create a custom plugin that applies the C++ plugin, then set compile flags, link flags, defines, etc.
However from inside my custom plugin, I can’t access the extensions added by the C++ plugin.

How can you access extension like the cppCompiler.args from inside a custom plugin that applies the ‘cpp’ plugin?

Thanks

1 Like

Hi Dror,

You need to write rules, see https://docs.gradle.org/current/userguide/software_model.html

Here is a sample rule that define a macro on the cppCompiler of all binaries:


class MyCppRules extends RuleSource {
    @Defaults
    void defaultMacros(@Each NativeBinarySpec binary) {
        binary.cppCompiler.define 'SOME_MACRO', 'some_value'
    }
}

apply plugin: 'cpp'
apply plugin: MyCppRules

model {
    components {
        main(NativeExecutableSpec)
    }
}

HTH

1 Like

Great - this seems to work.

One more related question - how do I write a test for my RuleSource?

I did something like this:
@Test
public void test() {
Project project = ProjectBuilder.builder().build()
project.pluginManager.apply 'cpp’
project.pluginManager.apply 'visual-studio’
project.pluginManager.apply ‘com.amd.devtools.cppproject’
// TODO what assert to do
}

How can I access the binary spec from the test?

Hi Dror,

You can have a look at NativeComponentPluginTest from the Gradle source. It demonstrates how to write unit tests against the native software model using ProjectBuilder.

Please note that you can also write integration tests using TestKit. Integration tests shall be more easy to reason about and closer to actual build users use cases.

Hi

First thank for the answers - Using these I’ve been able to create a simple plugin that applies most of the settings I want.

I still have a few questions

  1. How do I access model children that are not a native binary spec, e.g. I want to define default buildTypes, default platforms ,etc. I saw there is an option to use something called a model path. But I can’t find out how to use it.
  2. When setting default behavior for the native binary spec, the toolchain is always null. This prevent setting default values based on toolchain. Should this be done not in a @Defaults method, but in some other type of method?

It would be nice to have an example how to use RuleSource to override an existing model.

Thanks
Dror

Great!

  1. How do I access model children that are not a native binary spec, e.g. I want to define default buildTypes, default platforms ,etc. I saw there is an option to use something called a model path. But I can’t find out how to use it.

You are looking for BuildTypesContainer, here is a rule that adds some default build types:

@Defaults
void defaultsBuildTypes(BuildTypeContainer buildTypes) {
    buildTypes.create("community");
    buildTypes.create("enterprise");
}
  1. When setting default behavior for the native binary spec, the toolchain is always null. This prevent setting default values based on toolchain. Should this be done not in a @Defaults method, but in some other type of method?

Yes, you can use @Mutate instead of @Defaults so your rule runs at a later stage. Then the NativeBinarySpecs will have their toolchain set.

Great.

There is no platform container - can I use NamedDomainObjectContainer to get the platform container?
In general use NamedDomainObjectContainer to get the model T container?

Thanks

Answering my own question - use PlatformContainer
:wink:

2 Likes

Hi guys,

I’d like to do something very similar for an internal project. I already created and validated a Gradle configuration that allows me to cross-compile code for a specific processor. I want to convert that Gradle Configuration into a plugin that I can apply to any build.gradle in order to cross-compile automatically for our processor. This plugin will set the cross-compilers toolchains, set the locations of src folders, set flags, etc. To give you an idea, here is the kind of build.gradle I want to convert into a plugin: Cross-compiler for C project, commandLine vs Gradle

I read your answer Paul but according to the Gradle doc, Rule based configuration will be deprecated and new plugins should not use this concept… well your answer is two years old now so it makes sense. Can you or someone else tell me what replace the Rule based configuration? And if possible share an example of code? I searched on the forum and the doc but have yet to find the info.

Thanks

Answering my own question. A good solution is to extend the CPlugin or CppPlugin e.g.

class MyCPlugin extends CPlugin {      
  @Override     
     void apply(Project project) {
         super.apply(project)
           components {
             mainARM(NativeExecutableSpec) { // component 'main'
                targetPlatform 'ARM'
              }
           // Args and all here
         }
     }
 }

Here’s some information on the replacement non-software model native plugins https://blog.gradle.org/introducing-the-new-cpp-plugins
https://github.com/gradle/gradle-native

The typical pattern for applying other plugins is to apply them during your plugin’s apply method rather than using inheritance. This pattern is especially useful when it comes time for your plugin to apply more than one “child” plugin.

class MyCPlugin implements Plugin<Project>
    @Override
    void apply(Project project) {
        project.apply plugin: 'c'
        ...
    }
}

EDIT:
Using inheritance also opens your plugin up to a bug where the ‘c’ plugin can be applied to a project twice (most likely resulting in a failure). If the build applies both your plugin and the ‘c’ plugin directly, then the plugin manager won’t know about your call to CPlugin.apply. Using project.apply instead allows the plugin manager to filter out duplicate applications of the plugin.

1 Like

Hi Chris,

Thanks for the feedback, I applied your suggestion and it works well! I have been working with Gradle for a few weeks so I am still in the learning phase and I really appreciate your answer.

Would you mind advising me on the best way to apply more than one “child” plugin to my “main” plugin? Each “child” plugin apply a specific implements Plugin<Project> and has specific tasks.

If the child plugins have plugin ID files in META-INF/gradle-plugins, then you can apply them using their IDs:

class MyCPlugin implements Plugin<Project>
    @Override
    void apply(Project project) {
        project.apply plugin: 'c'
        project.apply plugin: 'com.prganization.mychildplugin1'
        project.apply plugin: 'com.prganization.mychildplugin2'
        ...
    }
}

If they do not have plugin ID files (or even if they do), you can apply them using their classes:

class MyCPlugin implements Plugin<Project>
    @Override
    void apply(Project project) {
        project.apply plugin: 'c'
        project.apply plugin: MyChildPlugin1
        project.apply plugin: MyChildPlugin2
        ...
    }
}
1 Like

Great, it works well. Again, thank you Chris!