Squashed 'third_party/allwpilib/' content from commit b0167e633

Change-Id: I5653017a690eec1917e8fff9017195d8af307926
git-subtree-dir: third_party/allwpilib
git-subtree-split: b0167e6337135545e7053acb89dd5726accc7dec
diff --git a/shared/config.gradle b/shared/config.gradle
new file mode 100644
index 0000000..7952018
--- /dev/null
+++ b/shared/config.gradle
@@ -0,0 +1,144 @@
+import org.gradle.internal.os.OperatingSystem
+
+nativeUtils.addWpiNativeUtils()
+nativeUtils.withRoboRIO()
+nativeUtils.withRaspbian()
+nativeUtils.withBionic()
+nativeUtils {
+  wpi {
+    configureDependencies {
+      wpiVersion = "-1"
+      niLibVersion = "2020.10.1"
+      opencvVersion = "3.4.7-2"
+      googleTestVersion = "1.9.0-4-437e100-1"
+      imguiVersion = "1.72b-2"
+    }
+  }
+}
+
+nativeUtils.wpi.addWarnings()
+nativeUtils.wpi.addWarningsAsErrors()
+
+nativeUtils.setSinglePrintPerPlatform()
+
+model {
+    components {
+        all {
+            nativeUtils.useAllPlatforms(it)
+        }
+    }
+    binaries {
+        withType(NativeBinarySpec).all {
+            nativeUtils.usePlatformArguments(it)
+        }
+    }
+}
+
+ext.appendDebugPathToBinaries = { binaries->
+    binaries.withType(StaticLibraryBinarySpec) {
+        if (it.buildType.name.contains('debug')) {
+            def staticFileDir = it.staticLibraryFile.parentFile
+            def staticFileName = it.staticLibraryFile.name
+            def staticFileExtension = staticFileName.substring(staticFileName.lastIndexOf('.'))
+            staticFileName = staticFileName.substring(0, staticFileName.lastIndexOf('.'))
+            staticFileName = staticFileName + 'd' + staticFileExtension
+            def newStaticFile = new File(staticFileDir, staticFileName)
+            it.staticLibraryFile = newStaticFile
+        }
+    }
+    binaries.withType(SharedLibraryBinarySpec) {
+        if (it.buildType.name.contains('debug')) {
+            def sharedFileDir = it.sharedLibraryFile.parentFile
+            def sharedFileName = it.sharedLibraryFile.name
+            def sharedFileExtension = sharedFileName.substring(sharedFileName.lastIndexOf('.'))
+            sharedFileName = sharedFileName.substring(0, sharedFileName.lastIndexOf('.'))
+            sharedFileName = sharedFileName + 'd' + sharedFileExtension
+            def newSharedFile = new File(sharedFileDir, sharedFileName)
+
+            def sharedLinkFileDir = it.sharedLibraryLinkFile.parentFile
+            def sharedLinkFileName = it.sharedLibraryLinkFile.name
+            def sharedLinkFileExtension = sharedLinkFileName.substring(sharedLinkFileName.lastIndexOf('.'))
+            sharedLinkFileName = sharedLinkFileName.substring(0, sharedLinkFileName.lastIndexOf('.'))
+            sharedLinkFileName = sharedLinkFileName + 'd' + sharedLinkFileExtension
+            def newLinkFile = new File(sharedLinkFileDir, sharedLinkFileName)
+
+            it.sharedLibraryLinkFile = newLinkFile
+            it.sharedLibraryFile = newSharedFile
+        }
+    }
+}
+
+ext.createComponentZipTasks = { components, names, base, type, project, func ->
+    def stringNames = names.collect {it.toString()}
+    def configMap = [:]
+    components.each {
+        if (it in NativeLibrarySpec && stringNames.contains(it.name)) {
+            it.binaries.each {
+                if (!it.buildable) return
+                def target = nativeUtils.getPublishClassifier(it)
+                if (configMap.containsKey(target)) {
+                    configMap.get(target).add(it)
+                } else {
+                    configMap.put(target, [])
+                    configMap.get(target).add(it)
+                }
+            }
+        }
+    }
+    def taskList = []
+    def outputsFolder = file("$project.buildDir/outputs")
+    configMap.each { key, value ->
+        def task = project.tasks.create(base + "-${key}", type) {
+            description = 'Creates component archive for platform ' + key
+            destinationDirectory = outputsFolder
+            classifier = key
+            archiveBaseName = '_M_' + base
+            duplicatesStrategy = 'exclude'
+
+            from(licenseFile) {
+                into '/'
+            }
+
+            func(it, value)
+        }
+        taskList.add(task)
+
+        project.build.dependsOn task
+
+        project.artifacts {
+            task
+        }
+        addTaskToCopyAllOutputs(task)
+    }
+    return taskList
+}
+
+ext.includeStandardZipFormat = { task, value ->
+    value.each { binary ->
+        if (binary.buildable) {
+            if (binary instanceof SharedLibraryBinarySpec) {
+                task.dependsOn binary.tasks.link
+                task.from(new File(binary.sharedLibraryFile.absolutePath + ".debug")) {
+                    into nativeUtils.getPlatformPath(binary) + '/shared'
+                }
+                def sharedPath = binary.sharedLibraryFile.absolutePath
+                sharedPath = sharedPath.substring(0, sharedPath.length() - 4)
+
+                task.from(new File(sharedPath + '.pdb')) {
+                    into nativeUtils.getPlatformPath(binary) + '/shared'
+                }
+                task.from(binary.sharedLibraryFile) {
+                    into nativeUtils.getPlatformPath(binary) + '/shared'
+                }
+                task.from(binary.sharedLibraryLinkFile) {
+                    into nativeUtils.getPlatformPath(binary) + '/shared'
+                }
+            } else  if (binary instanceof StaticLibraryBinarySpec) {
+                task.dependsOn binary.tasks.createStaticLib
+                task.from(binary.staticLibraryFile) {
+                    into nativeUtils.getPlatformPath(binary) + '/static'
+                }
+            }
+        }
+    }
+}
diff --git a/shared/cppDesktopTestTask.gradle b/shared/cppDesktopTestTask.gradle
new file mode 100644
index 0000000..04b7224
--- /dev/null
+++ b/shared/cppDesktopTestTask.gradle
@@ -0,0 +1,21 @@
+model {

+    tasks {

+        def ts = $.testSuites

+        project.tasks.register('testDesktopCpp') { testTask->

+            def systemArch = getCurrentArch()

+            def found = false

+            ts.each {

+                if (it in GoogleTestTestSuiteSpec && it.name == "${nativeName}Test") {

+                    it.binaries.each {

+                        if (found) return

+                        def arch = it.targetPlatform.name

+                        if (arch == systemArch && it.buildType.name == 'debug') {

+                            testTask.dependsOn it.tasks.run

+                            found = true

+                        }

+                    }

+                }

+            }

+        }

+    }

+}

diff --git a/shared/examplecheck.gradle b/shared/examplecheck.gradle
new file mode 100644
index 0000000..3d05c67
--- /dev/null
+++ b/shared/examplecheck.gradle
@@ -0,0 +1,86 @@
+def fileCheck = { parsedJson, folder ->
+    def folderNames = parsedJson.collect { it.foldername }
+    def folders = []
+    folder.eachDir {
+        folders << it.name
+    }
+    def disjunct = (folders + folderNames) - folders.intersect(folderNames)
+    def missingFromFolders = folderNames.intersect(disjunct)
+    def missingFromJson = folders.intersect(disjunct)
+
+    if (!missingFromFolders.empty || !missingFromJson.empty) {
+        StringBuilder missingString = new StringBuilder();
+        missingString.append("Missing From Folders\n")
+        for (String symbol : missingFromFolders) {
+            missingString.append(symbol);
+            missingString.append('\n');
+        }
+        missingString.append("\nMissing from JSON\n")
+        for (String symbol : missingFromJson) {
+            missingString.append(symbol);
+            missingString.append('\n');
+        }
+        throw new GradleException("Found missing items\n" + missingString.toString());
+    }
+}
+
+task checkTemplates(type: Task) {
+    doLast {
+        def parsedJson = new groovy.json.JsonSlurper().parseText(templateFile.text)
+        fileCheck(parsedJson, templateDirectory)
+        parsedJson.each {
+            assert it.name != null
+            assert it.description != null
+            assert it.tags != null
+            assert it.foldername != null
+            assert it.gradlebase != null
+            assert it.commandversion != null
+            if (it.gradlebase == 'java') {
+                assert it.mainclass != null
+            }
+        }
+    }
+}
+
+task checkExamples(type: Task) {
+    doLast {
+        def parsedJson = new groovy.json.JsonSlurper().parseText(exampleFile.text)
+        fileCheck(parsedJson, exampleDirectory)
+        parsedJson.each {
+            assert it.name != null
+            assert it.description != null
+            assert it.tags != null
+            assert it.foldername != null
+            assert it.gradlebase != null
+            assert it.commandversion != null
+            if (it.gradlebase == 'java') {
+                assert it.mainclass != null
+            }
+        }
+    }
+}
+
+task checkCommands(type: Task) {
+    doLast {
+        def parsedJson = new groovy.json.JsonSlurper().parseText(commandFile.text)
+        fileCheck(parsedJson, commandDirectory)
+        parsedJson.each {
+            assert it.name != null
+            assert it.description != null
+            assert it.tags != null
+            assert it.foldername != null
+            assert it.replacename != null
+            assert it.commandversion != null
+            if (project.isCppCommands) {
+                assert it.headers != null
+                assert !it.headers.isEmpty()
+                assert it.source != null
+                assert !it.source.isEmpty()
+            }
+        }
+    }
+}
+
+check.dependsOn checkTemplates
+check.dependsOn checkExamples
+check.dependsOn checkCommands
diff --git a/shared/googletest.gradle b/shared/googletest.gradle
new file mode 100644
index 0000000..ab3b51f
--- /dev/null
+++ b/shared/googletest.gradle
@@ -0,0 +1,7 @@
+model {
+    binaries {
+        withType(GoogleTestTestSuiteBinarySpec).all {
+            nativeUtils.useRequiredLibrary(it, 'googletest_static')
+        }
+    }
+}
diff --git a/shared/java/javacommon.gradle b/shared/java/javacommon.gradle
new file mode 100644
index 0000000..d83ead2
--- /dev/null
+++ b/shared/java/javacommon.gradle
@@ -0,0 +1,130 @@
+apply plugin: 'maven-publish'
+apply plugin: 'java-library'
+//apply plugin: 'net.ltgt.errorprone'
+apply plugin: 'jacoco'
+
+def baseArtifactId = project.baseId
+def artifactGroupId = project.groupId
+def javaBaseName = "_GROUP_edu_wpi_first_${project.baseId}_ID_${project.baseId}-java_CLS"
+
+def outputsFolder = file("$project.buildDir/outputs")
+
+task sourcesJar(type: Jar, dependsOn: classes) {
+    classifier = 'sources'
+    from sourceSets.main.allSource
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+    classifier = 'javadoc'
+    from javadoc.destinationDir
+}
+
+task outputJar(type: Jar, dependsOn: classes) {
+    archiveBaseName = javaBaseName
+    destinationDirectory = outputsFolder
+    from sourceSets.main.output
+}
+
+task outputSourcesJar(type: Jar, dependsOn: classes) {
+    archiveBaseName = javaBaseName
+    destinationDirectory = outputsFolder
+    classifier = 'sources'
+    from sourceSets.main.allSource
+}
+
+task outputJavadocJar(type: Jar, dependsOn: javadoc) {
+    archiveBaseName = javaBaseName
+    destinationDirectory = outputsFolder
+    classifier = 'javadoc'
+    from javadoc.destinationDir
+}
+
+artifacts {
+    archives sourcesJar
+    archives javadocJar
+    archives outputJar
+    archives outputSourcesJar
+    archives outputJavadocJar
+}
+
+addTaskToCopyAllOutputs(outputSourcesJar)
+addTaskToCopyAllOutputs(outputJavadocJar)
+addTaskToCopyAllOutputs(outputJar)
+
+build.dependsOn outputSourcesJar
+build.dependsOn outputJavadocJar
+build.dependsOn outputJar
+
+project(':').libraryBuild.dependsOn build
+
+publishing {
+    publications {
+
+        java(MavenPublication) {
+            artifact jar
+            artifact sourcesJar
+            artifact javadocJar
+
+            artifactId = "${baseArtifactId}-java"
+            groupId artifactGroupId
+            version wpilibVersioning.version.get()
+        }
+    }
+}
+
+test {
+    useJUnitPlatform()
+    systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true'
+    testLogging {
+        events "failed"
+        exceptionFormat "full"
+    }
+    finalizedBy jacocoTestReport
+}
+
+if (project.hasProperty('onlylinuxathena') || project.hasProperty('onlylinuxraspbian') || project.hasProperty('onlylinuxaarch64bionic')) {
+    test.enabled = false
+}
+
+repositories {
+    mavenCentral()
+    //maven.url "https://oss.sonatype.org/content/repositories/snapshots/"
+}
+
+sourceSets {
+    dev
+}
+
+tasks.withType(JavaCompile).configureEach {
+    options.compilerArgs = ['--release', '11']
+}
+
+dependencies {
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
+    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2'
+    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2'
+
+    devImplementation sourceSets.main.output
+
+    //errorprone 'com.google.errorprone:error_prone_core:2.3.2-SNAPSHOT'
+    //errorproneJavac 'com.google.errorprone:error_prone_core:2.3.1'
+}
+
+task run(type: JavaExec) {
+    classpath = sourceSets.dev.runtimeClasspath
+
+    main = project.devMain
+}
+
+build.dependsOn devClasses
+
+jacoco {
+    toolVersion = "0.8.4"
+}
+
+jacocoTestReport {
+    reports {
+        xml.enabled true
+        html.enabled true
+    }
+}
diff --git a/shared/java/javastyle.gradle b/shared/java/javastyle.gradle
new file mode 100644
index 0000000..eec3796
--- /dev/null
+++ b/shared/java/javastyle.gradle
@@ -0,0 +1,20 @@
+
+apply plugin: 'checkstyle'
+
+checkstyle {
+    toolVersion = "8.12"
+    configDirectory = file("${project.rootDir}/styleguide")
+    config = resources.text.fromFile(new File(configDirectory.get().getAsFile(), "checkstyle.xml"))
+}
+
+if (!project.hasProperty('skipPMD')) {
+    apply plugin: 'pmd'
+
+    pmd {
+        toolVersion = '6.7.0'
+        consoleOutput = true
+        reportsDir = file("$project.buildDir/reports/pmd")
+        ruleSetFiles = files(new File(rootDir, "styleguide/pmd-ruleset.xml"))
+        ruleSets = []
+    }
+}
diff --git a/shared/javaDesktopTestTask.gradle b/shared/javaDesktopTestTask.gradle
new file mode 100644
index 0000000..766fa50
--- /dev/null
+++ b/shared/javaDesktopTestTask.gradle
@@ -0,0 +1,3 @@
+tasks.register('testDesktopJava') {
+  dependsOn test
+}
diff --git a/shared/javacpp/publish.gradle b/shared/javacpp/publish.gradle
new file mode 100644
index 0000000..5002def
--- /dev/null
+++ b/shared/javacpp/publish.gradle
@@ -0,0 +1,65 @@
+apply plugin: 'maven-publish'
+
+def outputsFolder = file("$buildDir/outputs")
+
+def baseArtifactId = nativeName
+def artifactGroupId = "edu.wpi.first.${nativeName}"
+def zipBaseName = "_GROUP_edu_wpi_first_${nativeName}_ID_${nativeName}-cpp_CLS"
+
+def licenseFile = file("$rootDir/license.txt")
+
+task cppSourcesZip(type: Zip) {
+    destinationDirectory = outputsFolder
+    archiveBaseName = zipBaseName
+    classifier = "sources"
+
+    from(licenseFile) {
+        into '/'
+    }
+
+    from('src/main/native/cpp') {
+        into '/'
+    }
+}
+
+task cppHeadersZip(type: Zip) {
+    destinationDirectory = outputsFolder
+    archiveBaseName = zipBaseName
+    classifier = "headers"
+
+    from(licenseFile) {
+        into '/'
+    }
+
+    from('src/main/native/include') {
+        into '/'
+    }
+}
+
+artifacts {
+    archives cppHeadersZip
+    archives cppSourcesZip
+}
+
+addTaskToCopyAllOutputs(cppSourcesZip)
+addTaskToCopyAllOutputs(cppHeadersZip)
+
+model {
+    publishing {
+        def taskList = createComponentZipTasks($.components, [nativeName], zipBaseName, Zip, project, includeStandardZipFormat)
+
+        publications {
+            cpp(MavenPublication) {
+                taskList.each {
+                    artifact it
+                }
+                artifact cppHeadersZip
+                artifact cppSourcesZip
+
+                artifactId = "${baseArtifactId}-cpp"
+                groupId artifactGroupId
+                version wpilibVersioning.version.get()
+            }
+        }
+    }
+}
diff --git a/shared/javacpp/setupBuild.gradle b/shared/javacpp/setupBuild.gradle
new file mode 100644
index 0000000..04086ad
--- /dev/null
+++ b/shared/javacpp/setupBuild.gradle
@@ -0,0 +1,152 @@
+apply plugin: 'cpp'
+apply plugin: 'google-test-test-suite'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+apply plugin: SingleNativeBuild
+apply plugin: ExtraTasks
+
+apply from: "${rootDir}/shared/config.gradle"
+
+ext {
+    baseId = nativeName
+    groupId = "edu.wpi.first.${nativeName}"
+}
+
+apply from: "${rootDir}/shared/java/javacommon.gradle"
+
+project(':').libraryBuild.dependsOn build
+
+ext {
+    staticGtestConfigs = [:]
+}
+
+staticGtestConfigs["${nativeName}Test"] = []
+
+apply from: "${rootDir}/shared/googletest.gradle"
+
+model {
+    components {
+        "${nativeName}Base"(NativeLibrarySpec) {
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp'
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/main/native/include'
+                    }
+                }
+            }
+            binaries.all {
+                if (it instanceof SharedLibraryBinarySpec) {
+                    it.buildable = false
+                    return
+                }
+                if (project.hasProperty('extraSetup')) {
+                    extraSetup(it)
+                }
+            }
+        }
+        "${nativeName}"(NativeLibrarySpec) {
+            sources {
+                cpp {
+                    source {
+                        srcDirs "${rootDir}/shared/singlelib"
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/main/native/include'
+                    }
+                }
+            }
+            appendDebugPathToBinaries(binaries)
+        }
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}Dev"(NativeExecutableSpec) {
+            targetBuildTypes 'debug'
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/dev/native/cpp'
+                        include '**/*.cpp'
+                        lib library: nativeName
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/dev/native/include'
+                    }
+                }
+            }
+        }
+    }
+    testSuites {
+        "${nativeName}Test"(GoogleTestTestSuiteSpec) {
+            for(NativeComponentSpec c : $.components) {
+                if (c.name == nativeName) {
+                    testing c
+                    break
+                }
+            }
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/test/native/cpp'
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/test/native/include', 'src/main/native/cpp'
+                    }
+                }
+            }
+        }
+    }
+    binaries {
+        withType(GoogleTestTestSuiteBinarySpec) {
+            lib library: nativeName, linkage: 'shared'
+        }
+    }
+    tasks {
+        def c = $.components
+        project.tasks.create('runCpp', Exec) {
+            group = 'WPILib'
+            description = "Run the ${nativeName}Dev executable"
+            def found = false
+            def systemArch = getCurrentArch()
+            c.each {
+                if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
+                    it.binaries.each {
+                        if (!found) {
+                            def arch = it.targetPlatform.name
+                            if (arch == systemArch) {
+                                dependsOn it.tasks.install
+                                commandLine it.tasks.install.runScriptFile.get().asFile.toString()
+                                def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib'
+                                test.dependsOn it.tasks.install
+                                test.systemProperty 'java.library.path', filePath
+                                test.environment 'LD_LIBRARY_PATH', filePath
+                                test.workingDir filePath
+                                run.dependsOn it.tasks.install
+                                run.systemProperty 'java.library.path', filePath
+                                run.environment 'LD_LIBRARY_PATH', filePath
+                                run.workingDir filePath
+
+                                found = true
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+apply from: "${rootDir}/shared/cppDesktopTestTask.gradle"
+apply from: "${rootDir}/shared/javaDesktopTestTask.gradle"
+
+tasks.withType(RunTestExecutable) {
+    args "--gtest_output=xml:test_detail.xml"
+    outputs.dir outputDir
+}
+
+apply from: "${rootDir}/shared/javacpp/publish.gradle"
diff --git a/shared/jni/publish.gradle b/shared/jni/publish.gradle
new file mode 100644
index 0000000..765302a
--- /dev/null
+++ b/shared/jni/publish.gradle
@@ -0,0 +1,114 @@
+import java.security.MessageDigest
+apply plugin: 'maven-publish'
+
+def outputsFolder = file("$buildDir/outputs")
+
+def baseArtifactId = nativeName
+def artifactGroupId = "edu.wpi.first.${nativeName}"
+def zipBaseName = "_GROUP_edu_wpi_first_${nativeName}_ID_${nativeName}-cpp_CLS"
+def jniBaseName = "_GROUP_edu_wpi_first_${nativeName}_ID_${nativeName}-jni_CLS"
+
+def licenseFile = file("$rootDir/license.txt")
+
+task cppSourcesZip(type: Zip) {
+    destinationDirectory = outputsFolder
+    archiveBaseName = zipBaseName
+    classifier = "sources"
+    duplicatesStrategy = 'exclude'
+
+    from(licenseFile) {
+        into '/'
+    }
+
+    from('src/main/native/cpp') {
+        into '/'
+    }
+
+    model {
+        components {
+            it.all {
+                if (it in getJniSpecClass()) {
+                    it.jniHeaderLocations.each {
+                        dependsOn it.key
+                        from(it.value) {
+                            into '/jni'
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+task cppHeadersZip(type: Zip) {
+    destinationDirectory = outputsFolder
+    archiveBaseName = zipBaseName
+    classifier = "headers"
+
+    from(licenseFile) {
+        into '/'
+    }
+
+    from('src/main/native/include') {
+        into '/'
+    }
+}
+
+artifacts {
+    archives cppHeadersZip
+    archives cppSourcesZip
+}
+
+addTaskToCopyAllOutputs(cppSourcesZip)
+addTaskToCopyAllOutputs(cppHeadersZip)
+
+model {
+    publishing {
+        def taskList = createComponentZipTasks($.components, [nativeName, "${nativeName}JNIShared"], zipBaseName, Zip, project, includeStandardZipFormat)
+
+        def jniTaskList = createComponentZipTasks($.components, ["${nativeName}JNI"], jniBaseName, Jar, project, { task, value ->
+            value.each { binary ->
+                if (binary.buildable) {
+                    if (binary instanceof SharedLibraryBinarySpec) {
+                        task.dependsOn binary.tasks.link
+                        def hashFile = new File(binary.sharedLibraryFile.parentFile.absolutePath, "${binary.component.baseName}.hash")
+                        task.outputs.file(hashFile)
+                        task.inputs.file(binary.sharedLibraryFile)
+                        task.from(hashFile) {
+                            into nativeUtils.getPlatformPath(binary)
+                        }
+                        task.doFirst {
+                            hashFile.text = MessageDigest.getInstance("MD5").digest(binary.sharedLibraryFile.bytes).encodeHex().toString()
+                        }
+                        task.from(binary.sharedLibraryFile) {
+                            into nativeUtils.getPlatformPath(binary)
+                        }
+                    }
+                }
+            }
+        })
+
+        publications {
+            cpp(MavenPublication) {
+                taskList.each {
+                    artifact it
+                }
+                artifact cppHeadersZip
+                artifact cppSourcesZip
+
+                artifactId = "${baseArtifactId}-cpp"
+                groupId artifactGroupId
+                version wpilibVersioning.version.get()
+            }
+            jni(MavenPublication) {
+                jniTaskList.each {
+                    artifact it
+                }
+
+                artifactId = "${baseArtifactId}-jni"
+                groupId artifactGroupId
+                version wpilibVersioning.version.get()
+            }
+        }
+    }
+}
diff --git a/shared/jni/setupBuild.gradle b/shared/jni/setupBuild.gradle
new file mode 100644
index 0000000..6c6148c
--- /dev/null
+++ b/shared/jni/setupBuild.gradle
@@ -0,0 +1,290 @@
+apply plugin: 'cpp'
+apply plugin: 'google-test-test-suite'
+apply plugin: 'visual-studio'
+apply plugin: 'edu.wpi.first.NativeUtils'
+apply plugin: 'edu.wpi.first.GradleJni'
+apply plugin: SingleNativeBuild
+apply plugin: ExtraTasks
+
+apply from: "${rootDir}/shared/config.gradle"
+
+ext {
+    baseId = nativeName
+    groupId = "edu.wpi.first.${nativeName}"
+}
+
+apply from: "${rootDir}/shared/java/javacommon.gradle"
+
+dependencies {
+    if (!project.hasProperty('noWpiutil')) {
+        implementation project(':wpiutil')
+        devImplementation project(':wpiutil')
+    }
+}
+
+project(':').libraryBuild.dependsOn build
+
+ext {
+    staticGtestConfigs = [:]
+}
+
+staticGtestConfigs["${nativeName}Test"] = []
+
+apply from: "${rootDir}/shared/googletest.gradle"
+
+model {
+    components {
+        "${nativeName}Base"(NativeLibrarySpec) {
+            if (project.hasProperty('setBaseName')) {
+                baseName = setBaseName
+            }
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp'
+                        include '**/*.cpp'
+                        exclude '**/jni/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDir 'src/main/native/include'
+                        if (project.hasProperty('generatedHeaders')) {
+                            srcDir generatedHeaders
+                        }
+                        include '**/*.h'
+                    }
+                }
+            }
+            binaries.all {
+                if (it instanceof SharedLibraryBinarySpec) {
+                    it.buildable = false
+                    return
+                }
+                if (!project.hasProperty('noWpiutil')) {
+                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                }
+                if (project.hasProperty('splitSetup')) {
+                    splitSetup(it)
+                }
+            }
+        }
+        "${nativeName}"(NativeLibrarySpec) {
+            if (project.hasProperty('setBaseName')) {
+                baseName = setBaseName
+            }
+            sources {
+                cpp {
+                    source {
+                        srcDirs "${rootDir}/shared/singlelib"
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDir 'src/main/native/include'
+                        if (project.hasProperty('generatedHeaders')) {
+                            srcDir generatedHeaders
+                        }
+                    }
+                }
+            }
+            if (!project.hasProperty('noWpiutil')) {
+                binaries.all {
+                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                }
+            }
+            appendDebugPathToBinaries(binaries)
+        }
+        "${nativeName}JNIShared"(JniNativeLibrarySpec) {
+            if (project.hasProperty('setBaseName')) {
+                baseName = setBaseName + 'jni'
+            } else {
+                baseName = nativeName + 'jni'
+            }
+
+            enableCheckTask true
+            javaCompileTasks << compileJava
+            jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.roborio)
+            jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.raspbian)
+            jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.aarch64bionic)
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp'
+                        include '**/jni/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDir 'src/main/native/include'
+                        if (project.hasProperty('generatedHeaders')) {
+                            srcDir generatedHeaders
+                        }
+                        include '**/*.h'
+                    }
+
+                }
+            }
+            binaries.all {
+                if (it instanceof StaticLibraryBinarySpec) {
+                    it.buildable = false
+                    return
+                }
+                lib library: "${nativeName}", linkage: 'shared'
+                if (!project.hasProperty('noWpiutil')) {
+                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                }
+                if (project.hasProperty('jniSplitSetup')) {
+                    jniSplitSetup(it)
+                }
+            }
+        }
+        "${nativeName}JNI"(JniNativeLibrarySpec) {
+            if (project.hasProperty('setBaseName')) {
+                baseName = setBaseName + 'jni'
+            } else {
+                baseName = nativeName + 'jni'
+            }
+
+            enableCheckTask true
+            javaCompileTasks << compileJava
+            jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.roborio)
+            jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.raspbian)
+            jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.aarch64bionic)
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/main/native/cpp'
+                        include '**/jni/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDir 'src/main/native/include'
+                        if (project.hasProperty('generatedHeaders')) {
+                            srcDir generatedHeaders
+                        }
+                        include '**/*.h'
+                    }
+                }
+            }
+            binaries.all {
+                if (it instanceof StaticLibraryBinarySpec) {
+                    it.buildable = false
+                    return
+                }
+                if (!project.hasProperty('noWpiutil')) {
+                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
+                }
+                if (project.hasProperty('jniSplitSetup')) {
+                    jniSplitSetup(it)
+                }
+            }
+        }
+        // By default, a development executable will be generated. This is to help the case of
+        // testing specific functionality of the library.
+        "${nativeName}Dev"(NativeExecutableSpec) {
+            targetBuildTypes 'debug'
+            sources {
+                cpp {
+
+                    source {
+                        srcDirs 'src/dev/native/cpp'
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDir 'src/main/native/include'
+                        if (project.hasProperty('generatedHeaders')) {
+                            srcDir generatedHeaders
+                        }
+                    }
+                }
+            }
+            binaries.all {
+                lib library: nativeName, linkage: 'shared'
+                lib library: "${nativeName}JNIShared", linkage: 'shared'
+                if (!project.hasProperty('noWpiutil')) {
+                    lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                }
+                if (nativeName == 'hal' && it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(it, 'netcomm_shared', 'chipobject_shared', 'visa_shared', 'ni_runtime_shared')
+                }
+            }
+        }
+    }
+    testSuites {
+        "${nativeName}Test"(GoogleTestTestSuiteSpec) {
+            for(NativeComponentSpec c : $.components) {
+                if (c.name == nativeName) {
+                    testing c
+                    break
+                }
+            }
+            sources {
+                cpp {
+                    source {
+                        srcDirs 'src/test/native/cpp'
+                        include '**/*.cpp'
+                    }
+                    exportedHeaders {
+                        srcDirs 'src/test/native/include', 'src/main/native/cpp'
+                        if (project.hasProperty('generatedHeaders')) {
+                            srcDir generatedHeaders
+                        }
+                    }
+                }
+            }
+        }
+    }
+    binaries {
+        withType(GoogleTestTestSuiteBinarySpec) {
+            lib library: nativeName, linkage: 'shared'
+            if (!project.hasProperty('noWpiutil')) {
+                lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                if (nativeName == 'hal' && it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                    nativeUtils.useRequiredLibrary(it, 'netcomm_shared', 'chipobject_shared', 'visa_shared', 'ni_runtime_shared')
+                }
+            }
+        }
+    }
+    tasks {
+        def c = $.components
+        project.tasks.create('runCpp', Exec) {
+            group = 'WPILib'
+            description = "Run the ${nativeName}Dev executable"
+            def found = false
+            def systemArch = getCurrentArch()
+            c.each {
+                if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
+                    it.binaries.each {
+                        if (!found) {
+                            def arch = it.targetPlatform.name
+                            if (arch == systemArch) {
+                                dependsOn it.tasks.install
+                                commandLine it.tasks.install.runScriptFile.get().asFile.toString()
+                                def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib'
+                                test.dependsOn it.tasks.install
+                                test.systemProperty 'java.library.path', filePath
+                                test.environment 'LD_LIBRARY_PATH', filePath
+                                test.workingDir filePath
+                                run.dependsOn it.tasks.install
+                                run.systemProperty 'java.library.path', filePath
+                                run.environment 'LD_LIBRARY_PATH', filePath
+                                run.workingDir filePath
+
+                                found = true
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+apply from: "${rootDir}/shared/cppDesktopTestTask.gradle"
+apply from: "${rootDir}/shared/javaDesktopTestTask.gradle"
+
+ext.getJniSpecClass = {
+    return JniNativeLibrarySpec
+}
+
+tasks.withType(RunTestExecutable) {
+    args "--gtest_output=xml:test_detail.xml"
+    outputs.dir outputDir
+}
+
+apply from: "${rootDir}/shared/jni/publish.gradle"
diff --git a/shared/opencv.gradle b/shared/opencv.gradle
new file mode 100644
index 0000000..ca3764d
--- /dev/null
+++ b/shared/opencv.gradle
@@ -0,0 +1,33 @@
+def opencvVersion = '3.4.7-2'
+
+if (project.hasProperty('useCpp') && project.useCpp) {
+    model {
+        binaries {
+            withType(NativeBinarySpec).all {
+                def binary = it
+                project.sharedCvConfigs.each {
+                    if (binary.component.name == it.key) {
+                        nativeUtils.useRequiredLibrary(binary, 'opencv_shared')
+                    }
+                }
+                project.staticCvConfigs.each {
+                    if (binary.component.name == it.key) {
+                        nativeUtils.useRequiredLibrary(binary, 'opencv_static')
+                    }
+                }
+            }
+        }
+    }
+}
+
+if (project.hasProperty('useJava') && project.useJava) {
+    dependencies {
+        implementation "edu.wpi.first.thirdparty.frc2020.opencv:opencv-java:${opencvVersion}"
+        if (!project.hasProperty('skipDev') || !project.skipDev) {
+            devImplementation "edu.wpi.first.thirdparty.frc2020.opencv:opencv-java:${opencvVersion}"
+        }
+        if (project.hasProperty('useDocumentation') && project.useDocumentation) {
+            javaSource "edu.wpi.first.thirdparty.frc2020.opencv:opencv-java:${opencvVersion}:sources"
+        }
+    }
+}
diff --git a/shared/plugins/publish.gradle b/shared/plugins/publish.gradle
new file mode 100644
index 0000000..ec160ed
--- /dev/null
+++ b/shared/plugins/publish.gradle
@@ -0,0 +1,75 @@
+apply plugin: 'maven-publish'
+
+def baseArtifactId = pluginName
+def artifactGroupId = 'edu.wpi.first.halsim'
+def zipBaseName = "_GROUP_edu_wpi_first_halsim_ID_${pluginName}_CLS"
+
+def outputsFolder = file("$project.buildDir/outputs")
+
+task cppSourcesZip(type: Zip) {
+    destinationDirectory = outputsFolder
+    archiveBaseName = zipBaseName
+    classifier = "sources"
+
+    from(licenseFile) {
+        into '/'
+    }
+
+    from('src/main/native/cpp') {
+        into '/'
+    }
+}
+
+task cppHeadersZip(type: Zip) {
+    destinationDirectory = outputsFolder
+    archiveBaseName = zipBaseName
+    classifier = "headers"
+
+    from(licenseFile) {
+        into '/'
+    }
+
+    from('src/main/native/include') {
+        into '/'
+    }
+}
+
+build.dependsOn cppSourcesZip
+build.dependsOn cppHeadersZip
+
+addTaskToCopyAllOutputs(cppSourcesZip)
+addTaskToCopyAllOutputs(cppHeadersZip)
+
+
+model {
+    publishing {
+        def pluginTaskList = createComponentZipTasks($.components, [pluginName], zipBaseName, Zip, project, { task, value ->
+            value.each { binary ->
+                if (binary.buildable) {
+                    if (binary instanceof SharedLibraryBinarySpec) {
+                        task.dependsOn binary.buildTask
+                        task.from(binary.sharedLibraryFile) {
+                            into nativeUtils.getPlatformPath(binary) + '/shared'
+                        }
+                    }
+                }
+            }
+        })
+
+        publications {
+            cpp(MavenPublication) {
+                pluginTaskList.each {
+                    artifact it
+                }
+
+                artifact cppHeadersZip
+                artifact cppSourcesZip
+
+
+                artifactId = baseArtifactId
+                groupId artifactGroupId
+                version wpilibVersioning.version.get()
+            }
+        }
+    }
+}
diff --git a/shared/plugins/setupBuild.gradle b/shared/plugins/setupBuild.gradle
new file mode 100644
index 0000000..af9b4c4
--- /dev/null
+++ b/shared/plugins/setupBuild.gradle
@@ -0,0 +1,105 @@
+apply plugin: 'cpp'
+apply plugin: 'edu.wpi.first.NativeUtils'
+apply plugin: ExtraTasks
+
+if (!project.hasProperty('onlylinuxathena')) {
+    ext.skiplinuxathena = true
+    apply from: "${rootDir}/shared/config.gradle"
+
+    model {
+        components {
+            "${pluginName}"(NativeLibrarySpec) {
+                sources {
+                    cpp {
+                        source {
+                            srcDirs = ['src/main/native/cpp']
+                            includes = ["**/*.cpp"]
+                        }
+                        exportedHeaders {
+                            srcDirs = ["src/main/native/include"]
+                        }
+                    }
+                }
+                binaries.all {
+                    if (it instanceof StaticLibraryBinarySpec) {
+                        it.buildable = false
+                        return
+                    }
+                    project(':hal').addHalDependency(it, 'shared')
+                    if (project.hasProperty('includeNtCore')) {
+                        lib project: ':ntcore', library: 'ntcore', linkage: 'shared'
+                    }
+                    if (project.hasProperty('includeWpiutil')) {
+                        lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                    }
+                }
+                appendDebugPathToBinaries(binaries)
+            }
+            "${pluginName}Dev"(NativeExecutableSpec) {
+                targetBuildTypes 'debug'
+                sources {
+                    cpp {
+                        source {
+                            srcDirs = ['src/dev/native/cpp']
+                            includes = ["**/*.cpp"]
+                        }
+                        exportedHeaders {
+                            srcDirs = ["src/dev/native/include"]
+                        }
+                    }
+                }
+                binaries.all {
+                    if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxraspbian')  && !project.hasProperty('onlylinuxaarch64bionic')) {
+                        project(':hal').addHalDependency(it, 'shared')
+                        lib library: pluginName
+                        if (project.hasProperty('includeNtCore')) {
+                            lib project: ':ntcore', library: 'ntcore', linkage: 'shared'
+                        }
+                        lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
+                        if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
+                            nativeUtils.useRequiredLibrary(it, 'netcomm_shared', 'chipobject_shared', 'visa_shared', 'ni_runtime_shared')
+                        }
+                    } else {
+                        it.buildable = false
+                    }
+                }
+            }
+        }
+    }
+
+    apply from: "${rootDir}/shared/plugins/publish.gradle"
+}
+
+model {
+    tasks {
+        def c = $.components
+        if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxraspbian') && !project.hasProperty('onlylinuxaarch64bionic')) {
+            project.tasks.create('runCpp', Exec) {
+                group = 'WPILib'
+                description = "Run the ${pluginName}Dev executable"
+                def found = false
+                def systemArch = getCurrentArch()
+                c.each {
+                    if (it in NativeExecutableSpec && it.name == "${pluginName}Dev") {
+                        it.binaries.each {
+                            if (!found) {
+                                def arch = it.targetPlatform.name
+                                if (arch == systemArch) {
+                                    dependsOn it.tasks.install
+                                    commandLine it.tasks.install.runScriptFile.get().asFile.toString()
+                                    // it.tasks.install.libs.each { lib ->
+                                    //     if (lib.name.contains(pluginName)) {
+                                    //         def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib' + File.separatorChar + lib.name
+                                    //         environment('HALSIM_EXTENSIONS', filePath)
+                                    //     }
+                                    // }
+                                    found = true
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/shared/resources.gradle b/shared/resources.gradle
new file mode 100644
index 0000000..21da736
--- /dev/null
+++ b/shared/resources.gradle
@@ -0,0 +1,50 @@
+ext.createGenerateResourcesTask = { name, prefix, namespace, project ->
+    def generatedOutputDir = file("$buildDir/generated/$name/cpp")
+
+    def inputDir = file("$projectDir/src/$name/native/resources")
+
+    if (!prefix.isEmpty()) prefix += '_'
+
+    def task = project.tasks.create("generateResources-$name") {
+        outputs.dir generatedOutputDir
+        inputs.dir inputDir
+
+        doLast {
+            generatedOutputDir.mkdirs()
+            inputDir.eachFile { inputFile ->
+                if (inputFile.name.startsWith('.')) return
+                def fileBytes = inputFile.bytes
+                def outputFile = file("$generatedOutputDir/${inputFile.name}.cpp")
+                def funcName = "GetResource_" + inputFile.name.replaceAll('[^a-zA-Z0-9]', '_')
+                outputFile.withWriter { out ->
+                    def inputBytes = inputFile.bytes
+                    out.print '''#include <stddef.h>
+#include <wpi/StringRef.h>
+extern "C" {
+static const unsigned char contents[] = { '''
+
+                    for (int i = 0; i < fileBytes.size(); i++) {
+                        out.print String.format('0x%02x', (int) fileBytes[i] & 0xff)
+                        out.print ', '
+                    }
+                    out.println """};
+const unsigned char* ${prefix}${funcName}(size_t* len) {
+  *len = ${fileBytes.size()};
+  return contents;
+}
+}"""
+                    if (!namespace.isEmpty()) {
+                        out.println "namespace ${namespace} {"
+                    }
+                    out.println """wpi::StringRef ${funcName}() {
+  return wpi::StringRef(reinterpret_cast<const char*>(contents), ${fileBytes.size()});
+}"""
+                    if (!namespace.isEmpty()) {
+                        out.println '}'
+                    }
+                }
+            }
+        }
+    }
+    return task
+}
diff --git a/shared/singlelib/singlelib.cpp b/shared/singlelib/singlelib.cpp
new file mode 100644
index 0000000..6c9476a
--- /dev/null
+++ b/shared/singlelib/singlelib.cpp
@@ -0,0 +1,6 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project.                                                               */
+/*----------------------------------------------------------------------------*/