// This is the main dtf builder. If you've decided to build dtf
// from source, this may help you. You can think of building dtf
// in 5 steps:
//
//   1. Building Man Pages
//     Man pages are generated by the Ruby gem ronn, so you'll need that.
//     The man pages are built by "generateMans" task and will be asserted by
//     the main .deb check. "cleanMans" to remove.
//
//   2. Building the included.tar Bundle
//     This bundles and builds several Java based projects, including
//     apktool, smali, axmlprinter, and abe. Should only need the
//     included gradlew's for these. "generateIncluded" will build them
//     all, and "cleanIncluded" to wipe away. This process uses 'make',
//     so you'll probably need the 'build-essential' package. More details
//     are in 'dtf-included/MakeFile'
//
//   3. Building the dtfClient
//     This generates a output APK using the Android buildchain, so you'll
//     need that. There are two outputs:
//
//       1. 'release' - A release-key signed version. You'll need to review
//          how I'm dealing with keys in the 'dtf-client-app/app/build.gradle'
//          file if you want to use the release APK. Use "buildReleaseApk"
//          task to build.
//       2. 'debug' - Used for quick builds, using builtin keys, no ProGuard.
//          I use this internally, you can if you want. Use "buildDebugApk"
//          task to build.
//
//     "cleanApk" task removes _any_ built APKs. If you're not careful, you
//     may end up with both a debug and release APK in the .deb. Preference
//     is always taken over a debug APK if found; you've been warned.
//
//   4. Building the Python package
//     This task requires a number of small tests to be performed using
//     flake8 and pylint. I chose to integrate these directly to keep the
//     code clean. You can manually disable these if you'd like, but they
//     are here to stay. "buildPython" generates the Python code that is
//     needed.
//
//   5. Generating the .deb
//     This is where all the asserts* I talk about come in to play. This final
//     step runs the necessary checks to make sure eveything is OK, then a
//     couple last minute checks, like checkBashisms + shellCheck on the
//     'bash-dtf' code. Unless you made heavy changes this shouldn't be an
//     issue. The end-game task here is "makeDeb".
//
// Putting this all together, to generate a release ready, full-featured
// .deb file, use:
//    $ ./gradlew generateMans \
//                generateIncluded \
//                buildReleaseApk \
//                buildPython \
//                makeDeb
//
// Misc 1: Testing dtf
//  Testing is currently handled by a combination of offline tests with
//  python-converage, python-runner, and venv, as well as additional Travis
//  tests. The Travis tests are a bit more tricky, but to run offline tests
//  and generate coverage, you can run the "doCoverage" task.
//
// Misc 2: Notes on cleaning
//  Since there are so many components, I have a number of cleans you can use:
//    clean - Just removes Python.
//    cleanApk - Removes the APK(s).
//    cleanFull - Removes /everything/.
//
// Enjoy dtf!

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "gradle.plugin.com.netflix.nebula:gradle-ospackage-plugin:3.6.1"
  }
}

apply plugin: "nebula.ospackage"

// Populate version information
String versionContents = new File("VERSION").text.trim()
def values = versionContents.split('-')

project.ext.set("buildVersion", values[0])
project.ext.set("buildRelease", values[1])

String versionString = "dtf-" + values[0] + "-" + values[1]

if ( values.length == 3) {
    project.ext.set("buildBranch", values[2])
    project.ext.set("versionString", versionString + "." + values[2])

} else {
    project.ext.set("buildBranch", "")
    project.ext.set("versionString", versionString)
}

// Date
String currentDate = new Date().format( 'yyyy-MM-dd' )

project.ext.set("currentDate", currentDate)


// Task Definitions
task makeDeb(type:Deb) {

        packageName = 'android-dtf'
        version = project.buildVersion
        release = project.buildRelease
        maintainer = 'Jake Valletta <javallet@gmail.com>'
        url = 'https://github.com/jakev/dtf'
        summary = 'Android Device Testing Framework (dtf)'
        packageDescription = 'Modular framework for discovering vulnerabilities on Android devices.'
        packageGroup = 'main'

        postUninstall file('deb-scripts/postuninstall')
        postInstall file('deb-scripts/postinstall')

        requires('python')
        requires('python-pip')

        into 'build/'

        // Move the wheel to a tmp dir
        from('python-dtf/dist/') {
            fileMode 0640
            into '/tmp/'
        }

        // Bash Completition
        from('bash-dtf/') {
            fileMode 0644
            into '/etc/bash_completion.d/'
            include '**/*.bash'
        }

        // dtf Bindings
        from('dtf-binding/') {
            fileMode 0755
            into '/usr/local/bin'
        }

        // Binder Links
        link('/usr/local/bin/dtf_aapt', '/usr/local/bin/dtfbinder', 0755)
        link('/usr/local/bin/dtf_abe', '/usr/local/bin/dtfbinder', 0755)
        link('/usr/local/bin/dtf_apktool', '/usr/local/bin/dtfbinder', 0755)
        link('/usr/local/bin/dtf_axmlprinter2', '/usr/local/bin/dtfbinder', 0755)
        link('/usr/local/bin/dtf_baksmali', '/usr/local/bin/dtfbinder', 0755)
        link('/usr/local/bin/dtf_busybox', '/usr/local/bin/dtfbinder', 0755)
        link('/usr/local/bin/dtf_shell', '/usr/local/bin/dtfbinder', 0755)
        link('/usr/local/bin/dtf_smali', '/usr/local/bin/dtfbinder', 0755)

        // Man pages
        from('man/') {
            into '/usr/local/man/man1/'
            fileMode 0644
            include '**/*.1'
        }

        from('man/') {
            into '/usr/local/man/man7/'
            fileMode 0644
            include '**/*.7'
        }

        // Bash support
        from('bash-dtf/') {
            into '/usr/local/lib/android-dtf/bash/'
            fileMode 0644
            include '**/*.sh'
        }

        // dtfClient
        from('dtf-client-app/app/build/outputs/apk/') {
            into '/usr/local/lib/android-dtf/dtfClient/'
            fileMode 0644
            include '**/*.apk'
        }

        // Included-tools TAR.
        from('dtf-included/build/') {
            into '/usr/local/lib/android-dtf/'
            fileMode 0644
            include '**/*.tar'
        }

    // This will assert the APK.
    doLast {
        try {
            if (new File("dtf-client-app/app/build/outputs/apk/").listFiles().count {} == 0) { }
        } catch (Exception e) {
            throw new GradleException('No client found. Use: build(Release|Debug)Apk')
        }
    }
}

// Run all tests
task doPythonTests() {}
task doBashTests() {}

// Build Python files
task buildPython(type:Exec) {

    workingDir 'python-dtf'
    commandLine 'python', 'setup.py', 'bdist_wheel'
}

// Clean the Python stuff
task cleanPython(type:Delete) {

    delete 'build'
    delete 'python-dtf/build'
    delete 'python-dtf/dist'
    delete "python-dtf/dtf.egg-info"
    delete 'python-dtf/dtf/VERSION'
}

// Flake8 the Code
task doFlake(type:Exec) {

    workingDir 'python-dtf'
    commandLine 'flake8', './dtf'
}

// pylint the Code
task doLint(type:Exec) {

    workingDir 'python-dtf'
    commandLine 'pylint', './dtf', '--rcfile=./pylint.config'
}

// Run pytest suite
task doPytests(type:Exec) {

    workingDir 'python-dtf'
    commandLine 'python', 'setup.py', 'test'
}

// Generate Coverage information
task doCoverage(type:Exec) {

    workingDir 'python-dtf'
    commandLine 'sh', 'generate_coverage.sh'
}

// Run checkbashisms
task doCheckBashisms {
    doLast {
      exec {
        workingDir 'bash-dtf'
        commandLine 'bash', '-c', 'checkbashisms -f ./*.sh'
      }
      exec {
        workingDir 'dtf-binding'
        commandLine 'bash', '-c', 'checkbashisms -f ./dtfbinder'
      }
    }
}

// Run shellcheck
task doShellCheck {
    doLast {
      exec {
        workingDir 'bash-dtf'
        commandLine 'bash', '-c', 'shellcheck ./*.sh'
      }
      exec {
        workingDir 'dtf-binding'
        commandLine 'bash', '-c', 'shellcheck ./dtfbinder'
      }
    }
}

// Copy version file
task copyVersion(type:Copy) {

    from('.')
    into 'python-dtf/dtf/'
    include('VERSION')
}

//// Man Pages
// Run `ronn` on man pages
task generateMans(type:Exec) {

    workingDir 'man'

    String commandString = new String("ronn ./*.ronn --date="
                                + project.currentDate
                                + " --organization=" + project.versionString
                                + " --manual=\"dtf Manual\" --roff")

    commandLine 'bash', '-c', commandString
}

// Remove generated man pages
task cleanMans(type:Delete) {

    delete fileTree('man/') {
        include '*.1'
        include '*.7'
    }
}

// Make sure man pages exist
task assertMans() {
    doLast {
        if (new File('man').listFiles().count { it.name ==~ /.*[1-9]$/ } == 0) {
            throw new GradleException('man pages not found. Use: generateMans')
        }
    }
}
//// End Man Related


//// Included Releated
// Use make to start the process
task generateIncluded(type:Exec) {

    workingDir 'dtf-included'

    String commandString = new String("make fatTar")

    commandLine 'bash', '-c', commandString
}

// Use make clean task to blow away included
task cleanIncluded(type:Exec) {

    workingDir 'dtf-included'

    String commandString = new String("make clean")

    commandLine 'bash', '-c', commandString
}

task assertIncluded() {
    doLast {
        if (new File('dtf-included/build').listFiles().count { it.name ==~ /.*\.tar$/ } == 0) {
            throw new GradleException('Included bundle not found. Use: generateIncluded')
        }
    }
}
//// End Included

//// dtfClient Releated
// Build release dtfClient APK
task buildReleaseApk(type:GradleBuild) {
    buildFile = 'dtf-client-app/build.gradle'
    tasks = ['assembleRelease']
}

// Build debug dtfClient APK
task buildDebugApk(type:GradleBuild) {
    buildFile = 'dtf-client-app/build.gradle'
    tasks = ['assembleDebug']
}

// Clean any APK
task cleanApk(type:GradleBuild) {
    buildFile = 'dtf-client-app/build.gradle'
    tasks = ['clean']
}
//// End dtfClient Related


// Remove generated API docs
task cleanApiDocs(type:Delete) {

    delete 'api-docs'
}

// Clean up
clean {}
task cleanFull {}

clean.dependsOn("cleanPython")

cleanFull.dependsOn("cleanApk")
cleanFull.dependsOn("cleanIncluded")
cleanFull.dependsOn("cleanMans")
cleanFull.dependsOn("cleanPython")

// Task Dependencies

doPythonTests.dependsOn doLint
doPythonTests.dependsOn doFlake

// Prepare the python code
buildPython.dependsOn doPythonTests
buildPython.dependsOn copyVersion

doBashTests.dependsOn doCheckBashisms
doBashTests.dependsOn doShellCheck

// Building Debian
makeDeb.dependsOn buildPython
makeDeb.dependsOn assertMans
makeDeb.dependsOn assertIncluded
makeDeb.dependsOn doBashTests

// Used to run coverage tests
doCoverage.dependsOn buildPython
