On 27 Nov 2013, at 8:38 pm, Michael Putters <michael.putt...@binarygenetics.com> wrote:
> Hello, > > I have started migrating a somewhat large (mostly) C++ project to Gradle, > coming from a set of Visual Studio solutions, various makefiles and random > oddities. Basically it has to build for Windows, Linux, OS X, iOS, Android > and Windows Phone, with some glue here and there on the mobile targets. > > Having spent some time with the latest Gradle builds, I have some questions > and suggestions. Keep in mind that those questions consider Gradle is already > capable of building for all those targets: just assume I'll provide the pull > requests to make it happen, I just need to know how it should be done. Excellent. Looking forward to more great pull requests. > > > 1. What is the simplest way to have a source set apply to only one target > platform? > > For example, with this setup: > > sources { > main { > cpp { } > objcpp { } > } > } > > targetPlatforms { > windows { } > ios { } > } > > How can I easily say "use the objcpp sourceset only for the ios target > platform"? Or possibly the other way around, "for the ios target platform, > use the cpp and objcpp sourcesets". I guess each has its own advantages :). The idea is to do a few things: 1. Allow you to declare which target operating systems make sense for a given source set. For example, you’d say things like: ‘this source targets iOS’ or ‘this source targets Windows XP or later’ or ‘this source is operating system agnostic’. In other words, you’d declare the operating system APIs as a kind of dependency of the source. When assembling a binary, Gradle would include only those source sets that make sense for the target platform of the binary. There’d be some implicit rules, such as: if the target operating system isn’t windows, don’t include the windows resources. 2. Possibly add some platform specific source sets and some conventions around where to find the source for each platform. For example, each component might have a source set for each target operating system, and this might default to include source files from `src/${component.name}/${os.name}/${language.name}` So, if you put your source in a certain directory, or configure the convention to whatever layout you happen to be using, then the platform specific source will just be handled automatically for you. 3. Finally, let you tweak all this for each native binary, so that you can write a rule which takes the source sets that Gradle has decided should be used for the binary and add or remove source sets from this. > > > 2. Could the DSL for target platforms be improved? Oh yes, it definitely could. > > Right now, platforms hold information about the architecture and operating > system. I believe it would make sense to extend this information for each > supported platform type in order to provide extra information for that > platform. Basically, you'd get something like this: > > targetPlatforms { > > windows(Win32) { > sdk '8.1' // include path for Windows > SDK 8.1 > version '6.2' // #define _WIN32_WINNT > 0x0602 (same as version target: '6.2') > unicode true // #define _UNICODE > icon name: 'MyApp' // fill the icon information > in a .rc > productName 'MyApp' // fill the product > information in a .rc (defaults to the project name) > productVersion '1.0.0.0' // fill the version > information in a .rc (defaults to the project version) > companyName 'My Company' // fill the company > information in a .rc (defaults to the project group) > } I’d probably bust this up into several pieces: - Stuff that’s specific to the target operating system: what it is and how to find the pieces you need to build things for that target operating system. - Stuff that’s specific to the binaries that you’re producing: things like product name and version and other meta-data that ends up baked into the binary. This stuff can be operating system specific, but it’s part of the definition of the binary, not the operating system. > > ios(iOS) { > sdk '7.1' // -isysroot version or path > version min: '6.0' target: '7.1' // minimum and target versions > frameworks 'Foundation', 'UIKit' // link with > sdk/System/Library/Framework/Foundation.framework/Foundation, … Frameworks are something specific to the binaries. I’d declare them as dependencies of the objective-c/c++ source sets. > icon name: 'MyApp' prerendered: true // fill the CFBundleIcons > information in a .plist > productName 'MyApp' // fill the product > information in a .plist (defaults to the project name) > productVersion '1.0.0.0' // fill the version > information in a .plist (defaults to the project version) > } > > android(Android) { > sdk '19' // Android SDK version or path > ndk 'r9b' // Android NDK version or path > version min: '17' target: '19' // minimum and target > versions (<uses-sdk/>) > icon name: 'MyApp' // fill the <application > android:icon="xx"/> in the Android manifest > productName 'MyApp' // fill the <application > android:label="xx"/> in the Android manifest (defaults to the project name) > productVersion '1.0.0.0' // fill the <manifest > android:versionName="xx"/> in the Android manifest (defaults to the project > version) > } > } > > targetArchitectures { > > x64 { > platforms targetPlatforms.windows, targetPlatforms.linux, > targetPlatforms.osx > } > > armv8 { > platforms targetPlatforms.ios, targetPlatforms.android > } > > } We have a slight terminology mismatch here. What you’re calling ‘target platform’ above we would call ‘target operating system’ (which isn’t part of the model yet) and what you’re calling ‘target architecture’ is what we’d call ‘target platform’. So, the plan is that target operating system + target architecture = target platform. So, you’d define a bunch of target operating systems, and for each of them define the target architectures, to come up with the set of target platforms. > > Right now the Windows SDK directory information is part of > VisualCppToolchain, but some people could want to build their Windows > application with other compilers. Right. The SDK defines what you’re building for, not how you’re building it, which means it belongs on the target operating system definition. > Also, in the example above, some of the properties are applicable to multiple > platforms. And having the architectures separated from the platforms means > you don't have to duplicate information just because you want both an x86 and > an x64 Win32 build. Absolutely. Introducing operating system as a separate concept will mean you can define stuff about a particular operating system (version) in a single place. > > > 3. How to properly share the resource.h file for Windows builds? > > I already mentioned it in https://github.com/gradle/gradle/pull/225 but it > makes more sense here: > > - .rc files generally include resource.h (which defines most of your resource > constants) > - said resource.h is then used from your C or C++ code to reference the > resources > > Right now, if I put resource.h in src/main/rc, it seems wrong to include > "../rc/resource.h" from src/main/cpp. Something like exportedHeaders seems > closer to the right solution but then it's just shared with another > sourceset, not really exported from that particular component. How could this > be made to work nicely? The idea would be that the resource sources have an associated set of headers that should be visible to the other source files in the same component, and possibly to other components. We might might generalise this so that: 1. Each source set can expose some API, and that API is visible (at least by default) to all the other source sets that make up the component. For C, C++, windows resources, Objective-C, Objective-C++, the API would be represented as a set of header files. The API for a source set can be empty. 2. Each source set might also have some private headers that are visible only to the source in that source set. Again, this can be empty, but often isn’t. 3. Similarly, a library component can expose some API that is visible to all other components which use the library. Exactly what this should be as a default is a bit of an open question. Possibly it’s empty by default, possibly it’s the union of the API of the source sets that make up the library, or possibly we use a convention where every library has a ‘headers’ source set that defines the API of the library. In this way, a source set is essentially a kind of light0weight library that is used as headers + source rather than as headers + binary, and which you can aggregate to form other libraries. > > > 4. Can we change/add some default values? > > Namely: > > 1) use src/xx/include instead of src/xx/headers, which is far more > conventional and intuitive to C/C++ developers Sure. > 2) add default include's to source sets: **/*.cpp for C++, **/*.c for C, etc This is part of the plan. Each language source set should have a filter which you can use to fine-tune how it is that we decide whether a given source file belongs to the source set or not. > > Right now, by default, we're compiling every header found in src/xx/cpp > (again not very intuitive). > > > That's about it for now :) Good feedback. Thanks. -- Adam Murdoch Gradle Co-founder http://www.gradle.org VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting http://www.gradleware.com