C++ Cross Compiling with mingw-w64: Is there a way to modify linker flags?

I’m running Arch 64-bit, and have installed the mingw-w64-toolchain package group. Using Gradle 2.1

I’ve gotten just about everything working after a few hours of google searching and debugging, it’s just I only have an issue with gradle not outputting shared libs files correctly for windows when cross-compiling (it wants to output a .so file instead of a .dll and a .a file).

Is there a way to have complete control over the linker flags when linking?

Ideally, the flags for the linker should be as seen here

My build.gradle can be seen here

(and yes I did get multilib compiling working.)

1 Like

Can you try it out with a recent nightly build? (http://www.gradle.org/nightly)

There have been recent improvements to cross-compilation support, including “Uses the file naming scheme of the target platform, rather than the host platform” (http://www.gradle.org/docs/nightly/release-notes.html#native-language-cross-compilation-improvements).

It appears that the nightly gradle-2.2-20140917220013+0000-all doesn’t want to recognize “cppCompiler”

FAILURE: Build failed with an exception.
  * Where:
Build file '/home/stephen/Documents/ResourcePacker/build_gradle_mingw64_release/build.gradle' line: 33
  * What went wrong:
A problem occurred configuring root project 'build_gradle_mingw64_release'.
> Exception thrown while executing model rule: model.toolChains
   > Could not find property 'cppCompiler' on Tool chain 'gcc' (GNU GCC).

I’ll see if I can figure out what’s going on in the meantime.

Ok got it working. Like you mentioned about the changes in the nightly release notes, I had to place the cppCompiler lines inside curlybraces after a call to eachPlatform(). I assume there’s a property that tells you what platform is the current one but I haven’t figured that out yet.

It seems it will create the .dll file but hasn’t made the .a file. I suspect it’s because my headers aren’t in the src/${name}/headers directory (the whole thing isn’t structured right because I am trying out Gradle after using CMake for my own C++ projects for a while). I’ll edit this post if I get that working too.

EDIT: Got it to generate a .a file, but with odd results. To get it to generate it at all, I had to manually add the flags that would make it generate the file.

libraries {
    ResourcePacker {
        targetPlatforms "linux_x86", "linux_x86_64", "windows_x86", "windows_x86_64"
          binaries.all {
            cppCompiler.args "-std=c++11", "-Wall", "-Wextra"
              if(buildType == buildTypes.debug) {
                cppCompiler.args "-O0", "-g"
            }
            if(buildType == buildTypes.release) {
                cppCompiler.define "NDEBUG"
                cppCompiler.args "-O3"
            }
            if(targetPlatform.getDisplayName().endsWith("'linux_x86'")) {
                cppCompiler.args "-m32", "-march=i686", "-mtune=generic"
                linker.args "-m32"
            }
            if(targetPlatform.getDisplayName().endsWith("'windows_x86'")) {
                cppCompiler.args "-I/usr/i686-w64-mingw32/include"//, "-nostdinc", "-nostdinc++"
                linker.args "-Wl,--out-implib,libResourcePacker.a"
            }
            if(targetPlatform.getDisplayName().endsWith("'windows_x86_64'")) {
                cppCompiler.args "-I/usr/x86_64-w64-mingw32/include"//, "-nostdinc", "-nostdinc++"
                linker.args "-Wl,--out-implib,libResourcePacker.a"
            }
           }
       }
   }

And when it did generate the file, it was placed in the same directory as the build.gradle script.

Hopefully this will be fixed in the future?

EDIT2: Oh, didn’t realize the property that I couldn’t figure out was the exact same name as what it’s for. “platform”. Unfortunately it’s a little finicky to check against (unless there’s also a very obvious method for it). So until I figure that out, I may use “platform.getDisplayName().endsWith(”‘linux_x86’")" for specific platforms.

There’s an easier way to specify arguments specific to a platform:

binaries.all {
  if (targetPlatform == platforms.windows_x86) {
    cppCompiler.args "-m32", "-march=i686", "-mtune=generic"
 }
}

For settings that are not specific to any particular binary, you can also configure your toolChain to target a particular platform:

model {
  toolChains {
    gcc(Gcc) {
      target("windows_x86") {
        cppCompiler.withArguments { List<String> args ->
          // Full control over the argument list here: insert/append/remove/replace
          args << "-m32"
        }
      }
    }
  }
}
1 Like

There’s an easier way to specify arguments specific to a platform:

binaries.all {
  if (targetPlatform == platforms.windows_x86) {
    cppCompiler.args "-m32", "-march=i686", "-mtune=generic"
 }
}

For settings that are not specific to any particular binary, you can also configure your toolChain to target a particular platform:

model {
  toolChains {
    gcc(Gcc) {
      target("windows_x86") {
        cppCompiler.withArguments { List<String> args ->
          // Full control over the argument list here: insert/append/remove/replace
          args << "-m32"
        }
      }
    }
  }
}

OK so I wasn’t aware of the ability to produce a GCC-compatible import library (’.a’) for a DLL produced by MinGW. This isn’t something Gradle does out of the box (yet).

From the docs you’ve linked, it seems like this isn’t necessary unless you’re linking against the DLL with a non-MinGW toolChain. So if you’re using Gradle to build the exe/dll that links to this DLL, it should work out of the box.

If you need to produce the import libraries for consumption by a different build tool, then you’ll need to specify an absolute path to the ‘.a’ file in your linker args. There is a property ‘binary.sharedLinkLibraryFile’ that could help:

binaries.all { binary ->
    def importLibPath = binary.sharedLibraryLinkFile.absolutePath
    // Can't get regular expression to display on forum...
    importLibPath.replace(".lib", ".a")
    println "Generating import lib: ${importLibPath}"
    linker.args "-Wl,--out-implib,${importLibPath}"
}

Good to know, thanks for the help!

it appears that binary.sharedLibraryLinkFile.absolutePath points to the .dll being generated. For now I’ll be using:

binaries.withType(SharedLibraryBinarySpec) { binary ->
            cppCompiler.define "BUILD_SHARED_LIBS"
            if(targetPlatform == platforms.windows_x86 || targetPlatform == platforms.windows_x86_64) {
                cppCompiler.define "ResourcePacker_EXPORTS"
                def importLibPath = binary.sharedLibraryLinkFile.absolutePath.replace(".dll", ".a")
                importLibPath = importLibPath.substring(0, importLibPath.lastIndexOf("ResourcePacker")) + "lib" + importLibPath.substring(importLibPath.lastIndexOf("ResourcePacker"))
                println "Generating import lib: ${importLibPath}"
                linker.args "-Wl,--out-implib,${importLibPath}"
            }
        }

OK sorry about that. You could also do ‘new File(binary.sharedLibraryLinkFile.parentFile, “libResourcePacker.a”)’ if you like that better.

Thanks for the report. I’ve created GRADLE-3171 to track the improvement to generate the import library for a DLL file with MinGW.