I have quite a complex project with many build variants and custom source sets. I have recently integrated the NDK and noticed something strange. Nothing is broken, everything seems to be working as it should. I'm thinking its a bug with Android Studio, unless something is wrong in the gradle config.
I added the following to my app/build.gradle
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
ndkVersion "22.0.6917172"
I then noticed that the directories within the "Project" pane of Android Studio were then displaying as if they were packages. It also looked as if every variant was being processed as well even though I am setting to be ignored.
This is what the directories looked like before
And this is what they look like now
The bigger issue is that custom source sets for build variants were being included at the app/src/ level with just the kotlin directory?
These are configured in the app/build.gradle like this example.
android {
...
sourceSets {
padoq.java.srcDirs += "src/standardTileHandler/kotlin"
}
...
}
padoq.java.srcDirs += nonHsdSource
Before, in project view, these appeared as the directory name with the packages inside, not with the kotlin directory at the root. Here is what it looked like before:
For completeness, I am ignoring certain build variants by setting the variant to be ignored in the root build.gradle's subprojects block:
subprojects {
afterEvaluate { project ->
if (project.hasProperty("android")) {
def androidProperty = project["android"]
if (androidProperty.hasProperty("flavorDimensionList")) {
def flavorDimensions = androidProperty["flavorDimensionList"]
if (flavorDimensions != null && flavorDimensions.contains("bopad")) {
androidProperty.invokeMethod("variantFilter", { variant ->
def names = variant.flavors*.name
// if names does not contain a variant that I want to build right now
setIgnore(true)
})
}
}
}
}
}
Related
I'm trying to create a small application that will be able to communicate with the AWS IoT service. Since I want it to be fairly small and I wanted to try something new, I decided to go for Kotlin Native. I quickly noticed that AWS has released their C++ library that allows you to easily connect to the AWS IoT service (https://github.com/aws/aws-iot-device-sdk-cpp/tree/release) I downloaded it and even managed to compile with MinGW (yes, I'm on Windows). I noticed that it generated a bunch of *.o files. I reckon this is now the right time to import it to my Kotlin Native project. My build.gradle file for now looks completely standard
plugins {
id 'kotlin-multiplatform' version '1.3.11'
}
repositories {
mavenCentral()
}
kotlin {
targets {
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
// For Linux, preset should be changed to e.g. presets.linuxX64
// For MacOS, preset should be changed to e.g. presets.macosX64
fromPreset(presets.mingwX64, 'mingw')
configure([mingw]) {
// Comment to generate Kotlin/Native library (KLIB) instead of executable file:
compilations.main.outputKinds('EXECUTABLE')
// Change to specify fully qualified name of your application's entry point:
compilations.main.entryPoint = 'sample.main'
}
}
sourceSets {
// Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
// in gradle.properties file and re-import your project in IDE.
mingwMain {
}
mingwTest {
}
}
}
task runProgram {
def buildType = 'release' // Change to 'debug' to run application with debug symbols.
dependsOn "link${buildType.capitalize()}ExecutableMingw"
doLast {
def programFile = kotlin.targets.mingw.compilations.main.getBinary('EXECUTABLE', buildType)
exec {
executable programFile
args ''
}
}
}
for some reason I cannot find any examples as to how to add my freshly complied dependency. Normally when you code C++ you have to specify the path to Include directory and the Lib directory separately. AFAIK this is not something that gradle provides out of the box. How can I import this dependency then? Or perhaps there's some centralised repository I could simply pull such dependency from, like in pretty much every other programming language that is still used nowadays? At least this specific library doesn't seem to be available on NuGet :/
Kotlin/Native is not[1] interoperable with C++ at this moment. You can however create C wrapper for any C++ library and it's functions from Kotlin/Native[2].
When using multiplatform gradle plugin you can define native interop with this syntax:
kotlin {
linuxX64 { // Replace with a target you need.
compilations.main {
cinterops {
myInterop {
// Def-file describing the native API.
// The default path is src/nativeInterop/cinterop/<interop-name>.def
defFile project.file("def-file.def")
// Package to place the Kotlin API generated.
packageName 'org.sample'
// Options to be passed to compiler by cinterop tool.
compilerOpts '-Ipath/to/headers'
// Directories for header search (an analogue of the -I<path> compiler option).
includeDirs.allHeaders("path1", "path2")
// Additional directories to search headers listed in the 'headerFilter' def-file option.
// -headerFilterAdditionalSearchPrefix command line option analogue.
includeDirs.headerFilterOnly("path1", "path2")
// A shortcut for includeDirs.allHeaders.
includeDirs("include/directory", "another/directory")
}
anotherInterop { /* ... */ }
}
}
}
}
If you only define the interop name the plugin will look for .def file[3] in src/nativeInterop/cinterop/ directory and use it (in this case src/nativeInterop/cinterop/myInterop.def).
kotlin {
linuxX64 {
compilations.main {
cinterops {
myInterop {
}
}
}
}
}
The .def files[3] contains information about the library you are trying to use and typically look like this.
headers = png.h
headerFilter = png.h
package = png
More information about cinterop: https://kotlinlang.org/docs/reference/native/c_interop.html
More information about multiplatform projects: https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html
Visual Studio 2017 provides built-in support for handling CMake projects. The documentation mostly covers scenarios based on pre-existing cmake projects. But is there any support for creating a cmake project without having to fiddle with the CMakeLists.txt file?
EDIT: VS2017 15.6 added an official New Project CMake Wizard
With version 15.6 came the feature of "Create CMake projects from the Add New Project dialog."
This creates a simple ninja based C++ "Hello CMake" project.
A Custom CMake Wizard
Your question and the lack of an existing Wizard inspired me to write one. It's a very basic setup and would most definitely benefit if people with more experience in writing Visual Studio extensions would contribute, but here it is:
https://github.com/FloriansGit/VSCMakeWizards
Edit: Latest VSIX installer is now also available for free on VS Marketplace
https://marketplace.visualstudio.com/items?itemName=oOFlorianOo.CMakeProjectWizards
The new "CMake Executable Template" will show up after a restart of your Visual Studio 2017 under "File/New/Project/Visual C++":
It generates the following files in the given folder and then uses "Open Folder" on it:
CMakeLists.txt
CMakeSettings.json
MyProject1.cpp
Next Steps
Possible next steps would be to:
Add an interactive Wizard Dialog for some basic project/compiler settings
Add also an Item Wizard to be able to add source files to the CMakeLists.txt
I'm looking forward getting feedback on the basic idea. Please add any requests directly to:
https://github.com/FloriansGit/VSCMakeWizards/issues
The Code
And here is the Wizards basic/initial code as a reference:
WizardImplementationClass.cs
// Based on https://learn.microsoft.com/en-us/visualstudio/extensibility/how-to-use-wizards-with-project-templates
// and https://stackoverflow.com/questions/3882764/issue-with-visual-studio-template-directory-creation
using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using EnvDTE80;
namespace VSCMakeWizards
{
public class WizardImplementation : IWizard
{
public void RunStarted(object automationObject,
Dictionary<string, string> replacementsDictionary,
WizardRunKind runKind, object[] customParams)
{
var destinationDir = replacementsDictionary["$destinationdirectory$"];
var desiredNamespace = replacementsDictionary["$safeprojectname$"];
var templatePath = Path.GetDirectoryName((string)customParams[0]);
var dte = automationObject as DTE2;
var solution = dte.Solution as EnvDTE100.Solution4;
if (solution.IsOpen)
{
solution.Close();
}
File.Copy(Path.Combine(templatePath, "CMakeSettings.json"), Path.Combine(destinationDir, "CMakeSettings.json"));
File.Copy(Path.Combine(templatePath, "main.cpp"), Path.Combine(destinationDir, desiredNamespace + ".cpp"));
// see https://stackoverflow.com/questions/1231768/c-sharp-string-replace-with-dictionary
Regex re = new Regex(#"(\$\w+\$)", RegexOptions.Compiled);
string input = File.ReadAllText(Path.Combine(templatePath, "CMakeLists.txt"));
string output = re.Replace(input, match => replacementsDictionary[match.Groups[1].Value]);
File.WriteAllText(Path.Combine(destinationDir, "CMakeLists.txt"), output);
var vsSolution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution7;
if (vsSolution != null)
{
vsSolution.OpenFolder(destinationDir);
}
throw new WizardCancelledException();
}
// This method is called before opening any item that
// has the OpenInEditor attribute.
public void BeforeOpeningFile(ProjectItem projectItem)
{
}
public void ProjectFinishedGenerating(Project project)
{
}
// This method is only called for item templates,
// not for project templates.
public void ProjectItemFinishedGenerating(ProjectItem
projectItem)
{
}
// This method is called after the project is created.
public void RunFinished()
{
}
// This method is only called for item templates,
// not for project templates.
public bool ShouldAddProjectItem(string filePath)
{
return false;
}
}
}
Note: The WizardCancelledException is necessary, because Visual Studio otherwise would try to generate/open an actual solution. An "Open Folder" kind of project wizard is not yet supported (no SDK API for this).
References
Issue with visual studio template & directory creation
C# String replace with dictionary
As far as I know, there is no Wizard to create a new CMake project, but it can be done by configuring a CMakeSettings.json file. https://blogs.msdn.microsoft.com/vcblog/2017/08/14/cmake-support-in-visual-studio-customizing-your-environment/
I created a plugin XYZ that must inject configuration in all projects (from the root).
My project is a native C++ project for Visual Studio. Hence Java dependencies are not available...
Here is my layout, both variant uses the common lib projLib. projVariant1 uses the plugin but projVariant2 does not.
myRoot
- projLib
- projVariant1 <- uses the plugin
- projVariant2
Build projVariant1 should produce: myRoot#XYZ, projLib#XYZ, projVariant1#XYZ
Build projVariant2 should produce: myRoot, projLib, projVariant2
My idea is to do something like this (pseudo code) :
def applyPluginXYZ(Project p) {
p.if_part_of_compilation { // <-- this does not exist
p.rootProject.subprojects {
// apply change
}
}
}
projVariant1's build.gradle :
apply plugin: 'cpp'
applyPluginXYZ(project)
I've been trying to figure out a proper way of leveraging the information already present in a solution file of a rather big c++ code base.
The problem I'm trying to solve is calling an external script on some of the project files within the solution, but observing the already present dependencies specified in the solution and single project files.
I've successfully called said (python) script by adding a custom target to one of my project files and using msbuild with the /t:{TargetName} command on the vcxproj file.
I could now add this target to every project file that needs to call this script and afterwards call msbuild for each of them in the correct order, however this defeats the purpose of leveraging the dependencies known by the solution.
Calling the solution with the custom target does however not work (as seen by other stackoverflow questions like this: Invoke Custom MSBuild Target on Solution File).
On the other hand, since I want to be able to ONLY call the script target and not being dependent on also calling some Build command, I cannot use the proposed workarounds in some of those answers of adding a Post or PreBuild target.
Is there any other way of using the dependencies without having to go the route of msbuild and custom targets, or is there an other workaround that could serve my purpose?
Is there any other way of using the dependencies without having to go the route of msbuild and custom targets, or is there an other workaround that could serve my purpose?
You can build the sln programmatically, Here is a console APP with c# for your reference.
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
using System;
namespace LoadAllProject
{
class Program
{
static void Main(string[] args)
{
ILogger logger = new ConsoleLogger();
string solutionPath = #"D:\Project\Msbuild\CppApp5\CppApp5.sln";
var solutionFile = SolutionFile.Parse(solutionPath);
foreach (var item in solutionFile.ProjectsInOrder)
{
Project project = ProjectCollection.GlobalProjectCollection.LoadProject(item.AbsolutePath);
project.SetGlobalProperty("Configuration", "Debug");
if (project.GetPropertyValue("RootNamespace") == "CppApp5")
{
project.Build(new[] { "Build", "Yourcustomtarget" }, new[] { logger });
}
else
{
project.Build(new[] { "Build" }, new[] { logger });
}
}
Console.ReadKey();
}
}
}
I have set up a project on top of Qt (so source is written in C++) and I wanted to try Gradle for automatic builds on that. It took me some time to get into the details of configuring a multi project build (there is an executable and two libs) and now I am trying to tell the cpp-exe and the cpp-lib plugin how my source tree is structured.
I have set up a task that should print all of the source sets (there should be at least the default ones right?) and it looks like this:
task projectinfo {
description = "Informations about the current project"
group = INFORMATIONS_GROUP
doFirst {
task -> print ("""${task.project.sourceSets.all}""")
}
If I run this task Gradle tells me that there is no property "sourceSets" for the project. The documentation of the plugin tells me that it is possible to customize the source locations, but not how.
So my question would be: How can I tell the Gradle cpp plugin which source files to use. If there is any documentation about the cpp plugin apart from its API documentation and the Gradle user guide that would be helping too.
Have a look at Adam Murdoch's usage of Gradle's 'cpp plugin'. I believe he's one of the main Gradle submitters, so he should know how to use this better than anyone:
Exert from native-platform/build.gradle
cpp {
sourceSets {
main {
source.exclude 'curses.cpp'
}
curses {
source.srcDirs = ['src/main/cpp']
source.include 'curses.cpp'
source.include 'generic.cpp'
source.include 'generic_posix.cpp'
}
}
}
Then, within the 'libraries' node, refer to all/any combination of architecture and source sets:
sourceSets << cpp.sourceSets.main
sourceSets << cpp.sourceSets.curses
I've not had too long to look over it myself, but it looks like he defines a number of OS architecture based source code include combinations and stores these in the variants variable. He then processes them into platform JARs (I haven't actually ran the build yet, maybe I should).
Also, take a look at https://github.com/rklaren/GradleJNI, it uses the cpp plugin but looks to be a little Windows-oriented.
Update - I also found https://github.com/alkemist/gradle-cpp-demo, which has an example of the 'cpp-exe' plugin building an executable.