Post-build step in QBS - build

I'm trying to convert the nRF51822 (an Arm Cortex microcontroller) build process from Make to QBS. I've got the compilation process working ok (lots of hard-coded paths but I'll fix that later). However, the final step is to use objcopy to convert the .out file produced by the linker to a .hex file. I can't work out how to get my rule to run other than by making another Application like this (which doesn't work very well):
import qbs
import qbs.FileInfo
Project {
CppApplication {
type: "application" // To suppress bundle generation on Mac
name: "nrf51_template"
files: ["main.cpp",
"nrf51822/Source/nrf_delay/nrf_delay.c"]
Group {
name: "Startup files"
files: ["nrf51822/Source/Templates/system_" + deviceSeries + ".c",
"nrf51822/Source/Templates/gcc/gcc_startup_" + deviceSeries + ".s"]
}
// The chip variant can be:
//
// "xxaa": The 256 kB version
// "xxbb": The 128 kB version
//
// RFduino is xxaa.
property string deviceVariant: "xxaa"
// Must be "nrf51"
property string deviceSeries: "nrf51"
// The softdevice (radio firmware) to use. Can be:
//
// "": For no radio.
// "s110": For BLE slave/peripheral
// "s120": For BLE host/central
// "s130": For BLE central and peripheral
property string softDevice: "s110"
// Must be cortex-m0
property string cpu: "cortex-m0"
cpp.includePaths: ["nrf51822/Include", "nrf51822/Include/gcc"]
cpp.compilerName: ["arm-none-eabi-g++.exe"]
cpp.compilerPath: ["C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/bin/arm-none-eabi-g++.exe"]
cpp.linkerName: ["arm-none-eabi-g++.exe"]
cpp.linkerPath: ["C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/bin/arm-none-eabi-g++.exe"]
cpp.cxxFlags: ["-mcpu=" + cpu, "-mthumb", "-mabi=aapcs", "--std=c++11", "-mfloat-abi=soft"]
cpp.linkerFlags: ["-L\"C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/arm-none-eabi/lib/armv6-m\"",
"-L\"C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/lib/gcc/arm-none-eabi/4.8.3/armv6-m\"",
"-Xlinker",
"-Map=C:/Users/thutt/nRF51_Template/output_filename.map",
"-mcpu=" + cpu,
"-mthumb",
"-mabi=aapcs",
"-L", "C:/Users/thutt/nRF51_Template/nrf51822/Source/templates/gcc/",
"-Tgcc_" + deviceSeries + "_" + softDevice + "_" + deviceVariant + ".ld"]
cpp.defines: ["BOARD_PCA10001", "NRF51"]
// Suppresses -m32 compiler option.
cpp.architecture: "arm"
// Suppress windows definitions and compiler options.
cpp.minimumWindowsVersion: undefined
cpp.executableSuffix: ".out"
// To flash:
// nrfjprog --reset --program $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).hex
}
Application {
name: "nrf51_template_hex"
Group {
files: "C:/Users/thutt/nRF51_Template-build/qtc_Desktop2-debug/nrf51_template.out"
fileTags: ["out"]
}
Depends {
name: "nrf51_template"
}
Rule {
id: hex
inputs: ["out"]
Artifact {
fileTags: ["application"]
fileName: ".obj/" + product.name + "/" + input.baseDir + "/" + input.fileName + ".hex"
}
prepare: {
// var compilerPath = ModUtils.moduleProperty(product, "compilerPath");
var objCopyPath = "C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/bin/arm-none-eabi-objcopy.exe";
var args = ["-O", "ihex", input.filePath, output.filePath];
var cmd = new Command(objCopyPath, args);
cmd.description = "converting to hex: " + FileInfo.fileName(input.filePath);
cmd.highlight = "linker";
return cmd;
}
}
}
}
Is it possible to have a post-link step in the CppApplication instead of two Applications like this? Am I better off defining a totally new Application with my own Rules for compilation and linking?
Also bonus question, is it possible to specify the "run" executable in the QBS file so that when I click run in QtCreator it actually runs nrfjprog.exe and flashes the code to the chip? (You can do this in the IDE but I'd prefer to be able to do it in the QBS file.)

Figured it out, the final targets are determined by the type: line in the Application so just change it to this:
import qbs
import qbs.FileInfo
Project {
CppApplication {
// The filetag to generate.
type: "hex"
name: "nrf51_template"
files: ["main.cpp",
"SaneSPI.cpp",
"SaneSPI.h",
"Timer.cpp",
"Timer.h",
"Delay.cpp",
"Delay.h",
"GPIO.cpp",
"GPIO.h"]
Group {
name: "Startup files"
files: ["nrf51822/Source/Templates/system_" + deviceSeries + ".c",
"nrf51822/Source/Templates/gcc/gcc_startup_" + deviceSeries + ".s"]
}
// The chip variant can be:
//
// "xxaa": The 256 kB version
// "xxbb": The 128 kB version
//
// RFduino is xxaa.
property string deviceVariant: "xxaa"
// Must be "nrf51"
property string deviceSeries: "nrf51"
// The softdevice (radio firmware) to use. Can be:
//
// "": For no radio.
// "s110": For BLE slave/peripheral
// "s120": For BLE host/central
// "s130": For BLE central and peripheral
property string softDevice: "s110"
// Must be cortex-m0
property string cpu: "cortex-m0"
cpp.includePaths: ["nrf51822/Include",
"nrf51822/Include/gcc",
"nrf51822/Include/" + softDevice]
cpp.compilerName: ["arm-none-eabi-g++.exe"]
cpp.compilerPath: ["C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/bin/arm-none-eabi-g++.exe"]
cpp.linkerName: ["arm-none-eabi-g++.exe"]
cpp.linkerPath: ["C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/bin/arm-none-eabi-g++.exe"]
cpp.cxxFlags: ["-mcpu=" + cpu, "-mthumb", "-mabi=aapcs", "--std=c++11", "-mfloat-abi=soft"]
cpp.linkerFlags: ["-L\"C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/arm-none-eabi/lib/armv6-m\"",
"-L\"C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/lib/gcc/arm-none-eabi/4.8.3/armv6-m\"",
"-Xlinker",
"-Map=C:/Users/thutt/nRF51_Template/output_filename.map",
"-mcpu=" + cpu,
"-mthumb",
"-mabi=aapcs",
"-L", "C:/Users/thutt/nRF51_Template/nrf51822/Source/templates/gcc/",
"-Tgcc_" + deviceSeries + "_" + softDevice + "_" + deviceVariant + ".ld"]
cpp.defines: ["BOARD_PCA10001", "NRF51"]
// Suppresses -m32 compiler option.
cpp.architecture: "arm"
// Suppress windows definitions and compiler options.
cpp.minimumWindowsVersion: undefined
cpp.executableSuffix: ".out"
Rule {
id: hex
inputs: ["application"]
Artifact {
fileTags: ["hex"]
fileName: ".obj/" + product.name + "/" + input.baseDir + "/" + input.fileName + ".hex"
}
prepare: {
// var compilerPath = ModUtils.moduleProperty(product, "compilerPath");
var objCopyPath = "C:/Program Files/GNU Tools ARM Embedded/4.8 2014q1/bin/arm-none-eabi-objcopy.exe";
var args = ["-O", "ihex", input.filePath, output.filePath];
var cmd = new Command(objCopyPath, args);
cmd.description = "converting to hex: " + FileInfo.fileName(input.filePath);
cmd.highlight = "linker";
return cmd;
}
}
// To flash:
// nrfjprog --reset --program $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).hex
}
}

Related

How to properly configure meson / ninja / qt creator for recent c++ version?

I have tried to run a code which uses smart pointers recently. As the title says, I use Ninja (version 1.10.2), Meson (0.57.2) and Qt Creator 4.14.2.
I have the following meson.build :
project('RayTracerDemo',
'cpp',
version: '0.0.0',
default_options: [
'c_std=c2x',
'cpp_std=c++20',
'warning_level=3',
'optimization=3',
'werror=true'
]
)
includeDir = include_directories([])
sources = files([
'main.cpp'
])
headers = files([])
raytracer_exe = executable(
'RayTracerDemo',
sources: sources + headers,
include_directories: includeDir,
dependencies: []
)
But I still get the following error :
/~/programs/RayTracerDemo/main.cpp:189: error: ‘make_unique’ is not a member of ‘std’
../main.cpp: In function ‘void render(const std::vector<Sphere>&)’:
../main.cpp:189:52: error: ‘make_unique’ is not a member of ‘std’
189 | threads[height * width + width] = std::make_unique<std::thread>([&]() -> void
| ^~~~~~~~~~~
../main.cpp:189:52: note: ‘std::make_unique’ is only available from C++14 onwards
for the following lines :
threads[height * width + width] = std::make_unique<std::thread>([&]() -> void
{
float xx = (2 * ((x + 0.5) * invWidth) - 1) * angle * aspectratio;
float yy = (1 - 2 * ((y + 0.5) * invHeight)) * angle;
Vec3f raydir(xx, yy, -1);
raydir.normalize();
*pixel = trace(Vec3f(0), raydir, spheres, 0);
});
threads being a vector declared like so:
std::vector<std::unique_ptr<std::thread>> threads(height * width);
Qt Creator documentation says that some features aren't supported using Meson here, but this does not include compiler version issues.
It looks that everything is OK with meson options, you just forgot to add < memory> header (yes, error message is a bit confusing):
#include <memory>
...
Also, it could be due to setting c_std option for C++ compiler, since this option is for C compiler.
It's also possible that build directory is not (re)configured properly. To check currently configured options and flags you can with:
$ meson configure <build dir>
And to reconfigure:
$ meson setup --reconfigure <build dir>
BTW (it't nor related to question), this looks strange:
threads[height * width + width] =
as it overlaps the vector, shouldn't it be ? :
threads[invHeight * width + invWidth] =
TL;DR
This basic code :
#include <iostream>
#include <memory>
int main()
{
std::cout << "Hello World!" << std::endl;
std::unique_ptr<int> a = std::make_unique<int>();
return 0;
}
will not work. Compiler tells me C++ 14 or plus is required to use make_unique. The issue is that I already put "project('ThreadPool', 'cpp', default_options : ['cpp_std=c++17'])" in the meson.build file, but for some reason this file is just not taken in account by Qt Creator : checking what are the build settings, the C++ version chosen is C++11.

How to use shared libraries with JenkinsPipelineUnit from current branch

I'm trying to use jenkinsPipelineUnit to test a JenkinsFile that exists in the same git repository as my shared libraries. This Jenkinsfile references shared libraries located in src. It appears that I must commit my shared library changes before I can test them even if I use localSource from within the retriever.
How can I load my shared libraries and unit test them without committing the code first?
Here is my current code that doesn't work:
def library = library().name('pipeline-utils')
.defaultVersion("master")
.allowOverride(true)
.implicit(false)
.targetPath(sharedLibs)
.retriever(localSource(sharedLibs))
.build()
helper.registerSharedLibrary(library)
try {
def script = runScript("pipelines/test.groovy")
}
I get this error:
file:/Users/<myuserid>/git/pipelines/test.groovy: 2:
Error on loading library pipeline-utils#myteam/pipelineUnitTest :
Directory /Users/<myuserid>/git/out/test/classes/com/company/test/pipeline-utils#myteam/pipelineUnitTest does not exists # line 2, column 1.
#Library("pipeline-utils#myteam/pipelineUnitTest") _
This isn't as easy as it sounds. JenkinsPipelineUnit isn't moving any more since one year while some interesting work is waiting on pull-requests. Here are the steps I had to go through to get this working locally, but also on jenkins where the name of my repository directory can be different each time.
1. Create a custom version of JenkinsPipelineUnit
I started from https://github.com/jenkinsci/JenkinsPipelineUnit/pull/75 but had to add some other changes. These are all the changes:
diff --git a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy
index f4eeb17..dc13b9c 100644
--- a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy
+++ b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy
## -18,7 +18,7 ## class LibraryConfiguration {
String targetPath
LibraryConfiguration validate() {
- if (name && defaultVersion && retriever && targetPath)
+ if (name && retriever && targetPath && ((retriever instanceof LocalSource || defaultVersion)))
return this
throw new IllegalStateException("LibraryConfiguration is not properly initialized ${this.toString()}")
}
diff --git a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy
index 120a316..a253f2d 100644
--- a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy
+++ b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy
## -117,11 +117,14 ## class LibraryLoader {
}
private static boolean matches(String libName, String version, LibraryConfiguration libraryDescription) {
+ if (libraryDescription.allowOverride) {
+ return true
+ }
if (libraryDescription.name == libName) {
if (version == null) {
return true
}
- if (libraryDescription.allowOverride || libraryDescription.defaultVersion == version) {
+ if (libraryDescription.defaultVersion == version) {
return true
}
}
diff --git a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy
index 61babde..4edca23 100644
--- a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy
+++ b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy
## -11,7 +11,13 ## class LocalSource implements SourceRetriever {
#Override
List<URL> retrieve(String repository, String branch, String targetPath) {
- def sourceDir = new File(sourceURL).toPath().resolve("$repository#$branch").toFile()
+ def sourceURLPath = new File(sourceURL).toPath()
+ def sourceDir
+ if (branch) {
+ sourceDir = sourceURLPath.resolve("$repository#$branch").toFile()
+ } else {
+ sourceDir = sourceURLPath.resolve(repository).toFile()
+ }
if (sourceDir.exists()) {
return [sourceDir.toURI().toURL()]
}
2. Register your current repository directory as your shared library
Inspired from: https://github.com/jimcroft/jenkinslib-example/blob/master/test/com/example/TestCase1.groovy
in your TestClass.groovy:
void setup() {
String repositoryDirectoryName = FilenameUtils.getName(System.getProperty("user.dir"))
String dirPath = new File( System.getProperty("user.dir") )
.getAbsoluteFile()
.getParentFile()
.getAbsolutePath()
// This next call bypasses registerSharedLibrary; to allow registering a library with a different directory name
helper.libraries.put('my-jenkins-library', library(repositoryDirectoryName)
.allowOverride(true)
.implicit(false)
.targetPath(dirPath)
.retriever(localSource(dirPath))
.build())

Android Studio + Spek integration

I'm trying to add Spek testing framework to my Android Studio project. Following the instructions Here, I ended up adding the following to my module build.gradle:
testCompile 'org.jetbrains.spek:spek-api:1.1.5'
testCompile 'junit:junit:4.12'
testCompile "org.junit.platform:junit-platform-runner:1.0.0"
testRuntimeOnly 'org.jetbrains.spek:spek-junit-platform-engine:1.1.5'
Then I annotated my test with #RunWith(JUnitPlatform::class)
However, when I try to run the test, I get:
org.junit.platform.commons.util.PreconditionViolationException: Cannot create Launcher without at least one TestEngine; consider adding an engine implementation JAR to the classpath
Any idea what am I missing?
(For future references)
To use Kotlin and Spek + JUnit5 in Android Studio you need the following:
In project's build.gradle you need to have:
buildscript {
ext.kotlin_version = '1.2.10'
ext.JUnit5_version = '1.0.30'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "de.mannodermaus.gradle.plugins:android-junit5:$JUnit5_version"
}
}
In module's build.gradle you need to have:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "de.mannodermaus.android-junit5"
android {
...
sourceSets {
test.java.srcDirs += 'src/test/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
}
project.ext {
spekVersion = "1.1.5"
}
dependencies {
...
//
// TESTS
testImplementation("org.jetbrains.spek:spek-api:$spekVersion") {
exclude group: "org.jetbrains.kotlin"
}
testImplementation("org.jetbrains.spek:spek-junit-platform-engine:$spekVersion") {
exclude group: "org.junit.platform"
exclude group: "org.jetbrains.kotlin"
}
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testImplementation junit5.unitTests()
// see https://github.com/mannodermaus/android-junit5#android-studio-workarounds
testCompileOnly junit5.unitTestsRuntime()
}
Simple Spek test
class ExampleSpekTest : Spek({
val x = 2
val y = 3
given("x = $x and y = $y") {
val sum = x + y
it("should be that x + y = 5") {
Assert.assertEquals(5, sum)
}
it("should be that x - y = -1") {
val subtract = x - y
Assert.assertEquals(-1, subtract)
}
}
})
See
- official documentation http://spekframework.org/
- official plugin for running specs from the IDE https://github.com/raniejade/spek-idea-plugin
- Tests using Spek Framework - BDD Style vs JUnit Style https://www.youtube.com/watch?v=asDZ_7ZUiX4
- Simple Android configuration of Spek and JUnit5 https://gist.github.com/Mugurell/088daf42a4d60240ba6993681e0537a5
Apparently it doesn't work well... I ended up using kotlintest which is much easier to integrate

How can I initialize an embedded Python interpreter with bytecode optimization from C++?

I am running an embedded Python 2.7 interpreter within my C++ project, and I would like to optimize the interpreter as much as possible. One way I would like to do this is by disabling my debug statements using the __debug__ variable. I would also like to realize any possible gains in performance due to running Python with bytecode optimizations (i.e. the -O flag).
This Stack Overflow question addresses the use of the __debug__ variable, and notes that it can be turned off by running Python with -O. However, this flag can obviously not be passed for an embedded interpreter that is created with C++ hooks.
Below is my embedded interpreter initialization code. The code is not meant to be run in isolation, but should provide a glimpse into the environment I'm using. Is there some way I can alter or add to these function calls to specify that the embedded interpreter should apply bytecode optimizations, set the __debug__ variable to False, and in general run in "release" mode rather than "debug" mode?
void PythonInterface::GlobalInit() {
if(GLOBAL_INITIALIZED) return;
PY_MUTEX.lock();
const char *chome = getenv("NAO_HOME");
const char *cuser = getenv("USER");
string home = chome ? chome : "", user = cuser ? cuser : "";
if(user == "nao") {
std::string scriptPath = "/home/nao/python:";
std::string swigPath = SWIG_MODULE_DIR ":";
std::string corePath = "/usr/lib/python2.7:";
std::string modulePath = "/lib/python2.7";
setenv("PYTHONPATH", (scriptPath + swigPath + corePath + modulePath).c_str(), 1);
setenv("PYTHONHOME", "/usr", 1);
} else {
std::string scriptPath = home + "/core/python:";
std::string swigPath = SWIG_MODULE_DIR;
setenv("PYTHONPATH", (scriptPath + swigPath).c_str(), 1);
}
printf("Starting initialization of Python version %s\n", Py_GetVersion());
Py_InitializeEx(0); // InitializeEx(0) turns off signal hooks so ctrl c still works
GLOBAL_INITIALIZED = true;
PY_MUTEX.unlock();
}
void PythonInterface::Init(VisionCore* core) {
GlobalInit();
PY_MUTEX.lock();
CORE_MUTEX.lock();
CORE_INSTANCE = core;
thread_ = Py_NewInterpreter();
PyRun_SimpleString(
"import pythonswig_module\n"
"pythonC = pythonswig_module.PythonInterface().CORE_INSTANCE.interpreter_\n"
"pythonC.is_ok_ = False\n"
"from init import *\n"
"init()\n"
"pythonC.is_ok_ = True\n"
);
CORE_MUTEX.unlock();
PY_MUTEX.unlock();
}
I solved this by setting the PYTHONOPTIMIZE environment variable prior to calling Py_InitializeEx(0):
/* ... snip ... */
setenv("PYTHONOPTIMIZE", "yes", 0);
printf("Starting initialization of Python version %s\n", Py_GetVersion());
Py_InitializeEx(0); // InitializeEx(0) turns off signal hooks so ctrl c still works
/* ... snip ... */

Android studio, assembleDebugTest cause Multiple dex files define

trying to run android tests in android studio 0.8.1
I can run assembleDebug and assembleTest properly. But when I try to run the android test it calles assembleDebug and assembleDebugTest and with the latter I get the problem with 'Multiple dex files define'
a few pictures:
and the build.gradle of the project
dependencies {
compile project(':libraries:someLib')
compile ('com.google.android.gms:play-services:5.+')
compile ('fr.avianey:facebook-android-api:+#aar')
compile ('com.fasterxml.jackson.core:jackson-databind:2.3.1')
compile ('com.fasterxml.jackson.core:jackson-core:2.3.1')
compile ('com.fasterxml.jackson.core:jackson-annotations:2.3.0')
compile fileTree(dir: 'libs', include: '*.jar')
//androidTestCompile 'junit:junit:4.10'
//androidTestCompile 'org.robolectric:robolectric:2.1.+'
//androidTestCompile 'com.squareup:fest-android:1.0.+'
//androidTestCompile 'org.powermock:powermock-api-mockito:1.5.1'
}
and the build.gradle of the "someLib"
dependencies {
compile ('com.android.support:appcompat-v7:19.1.+')
compile ('com.nineoldandroids:library:2.4.0')
compile (group: 'com.google.guava', name: 'guava', version: '16.0-rc1')
}
they both share the rest
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
}
}
apply plugin: 'com.android.library'
repositories {
mavenCentral()
}
android {
compileSdkVersion 19
buildToolsVersion '19.1.0'
defaultConfig {
minSdkVersion 9
targetSdkVersion 19
}
lintOptions {
abortOnError false
}
}
So far I tried 2 solutions that seems to work but I am pretty sure there has to be something better.
Anyway 1 solution:
tasks.whenTaskAdded { theTask ->
if("assembleDebugTest".toString().equals(theTask.name.toString())) {
def yourTaskName = "cleanLibs"
project.task(yourTaskName) << {
println "${project.buildDir}/intermediates/pre-dexed/test/debug/"
delete fileTree(dir: ("${project.buildDir}/intermediates/pre-dexed/test/debug/"))
}
theTask.dependsOn(yourTaskName)
def processTask = "preDexDebugTest"
project.(yourTaskName.toString()).dependsOn(processTask)
project.(processTask.toString()).dependsOn("compileDebugTestJava")
}
}
this removes the libs before they are created again, it is slow and ungly, let the libs be created, removes them and creates them again
solution 2:
tasks.whenTaskAdded { theTask ->
if("assembleDebugTest".toString().equals(theTask.name.toString())) {
def processTask = "preDexDebugTest"
project.(processTask.toString()).enabled = false
}
}
just skip the task that create the first libs, this is way better than the first one
then for both you have to extend
android {
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'com/flurry/org/codehaus/jackson/map/VERSION.txt'
exclude 'com/flurry/org/codehaus/jackson/impl/VERSION.txt'
exclude 'com/flurry/org/apache/avro/data/Json.avsc'
exclude 'META-INF/ASL2.0'
}
}
by whatever is twice