diff --git a/.taskcluster.yml b/.taskcluster.yml new file mode 100644 index 00000000..51b74009 --- /dev/null +++ b/.taskcluster.yml @@ -0,0 +1,80 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +version: 0 +allowPullRequests: public +tasks: +#################################################################################################### +# Task: Pull requests +#################################################################################################### + - provisionerId: '{{ taskcluster.docker.provisionerId }}' + workerType: '{{ taskcluster.docker.workerType }}' + extra: + github: + env: true + events: + - pull_request.opened + - pull_request.edited + - pull_request.synchronize + - pull_request.reopened + - push + scopes: + - "queue:create-task:aws-provisioner-v1/github-worker" + - "queue:scheduler-id:taskcluster-github" + payload: + maxRunTime: 3600 + deadline: "{{ '2 hours' | $fromNow }}" + image: 'mozillamobile/mentat:1.1' + command: + - /bin/bash + - '--login' + - '-cx' + - >- + export TERM=dumb + && git fetch {{ event.head.repo.url }} {{ event.head.repo.branch }} + && git config advice.detachedHead false + && git checkout {{event.head.sha}} + && python automation/taskcluster/decision_task_pull_request.py + features: + taskclusterProxy: true + metadata: + name: Mentat Android SDK - Pull Request + description: Building and testing the Mentat Android SDK - triggered by a pull request. + owner: '{{ event.head.user.email }}' + source: '{{ event.head.repo.url }}' +#################################################################################################### +# Task: Release +#################################################################################################### + - provisionerId: '{{ taskcluster.docker.provisionerId }}' + workerType: '{{ taskcluster.docker.workerType }}' + extra: + github: + events: + - release + scopes: + - "secrets:get:project/mentat/publish" + payload: + maxRunTime: 3600 + deadline: "{{ '2 hours' | $fromNow }}" + image: 'mozillamobile/mentat:1.1' + command: + - /bin/bash + - '--login' + - '-cx' + - >- + export TERM=dumb + && git fetch origin --tags + && git config advice.detachedHead false + && git checkout {{ event.version }} + && python automation/taskcluster/release/fetch-bintray-api-key.py + && cd sdks/android/Mentat + && ./gradlew --no-daemon clean library:assembleRelease + && ./gradlew bintrayUpload --debug + features: + taskclusterProxy: true + metadata: + name: Mentat Android SDK - Release ({{ event.version }}) + description: Building and publishing release versions. + owner: '{{ event.head.user.email }}' + source: '{{ event.head.repo.url }}' diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..5357bf88 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,21 @@ +# 0.10 (2018-07-26) + +* sdks/android compiled against: + * Kotlin standard library 1.2.41 + +* **API changes**: + * `store_open{_encrypted}` now accepts an error parameter; corresponding constructors changed to be factory functions. + +* [Commits](https://github.com/mozilla/mentat/compare/v0.9.0...v0.10.0) + +# 0.9 (2018-07-25) + +* sdks/android compiled against: + * Kotlin standard library 1.2.41 + +* **API changes**: + * Mentat partitions now enforce their integrity, denying entids that aren't already known. + +* **sdks/android**: First version published to nalexander's personal bintray repository. +* Various bugfixes and refactorings (see commits below for details) +* [Commits](https://github.com/mozilla/mentat/compare/v0.8.1...v0.9.0) diff --git a/Cargo.toml b/Cargo.toml index f408a849..165a083e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ authors = [ "Thom Chiovoloni ", ] name = "mentat" -version = "0.8.1" +version = "0.10.0" build = "build/version.rs" [features] diff --git a/automation/docker/Dockerfile b/automation/docker/Dockerfile new file mode 100644 index 00000000..94e01bb2 --- /dev/null +++ b/automation/docker/Dockerfile @@ -0,0 +1,95 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +FROM mozillamobile/android-components:1.4 + +MAINTAINER Nick Alexander "nalexander@mozilla.com" + +#---------------------------------------------------------------------------------------------------------------------- +#-- Configuration ----------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------- + +ENV ANDROID_NDK_VERSION "r17b" +ENV PROJECT_REPOSITORY "https://github.com/mozilla/mentat.git" + +#---------------------------------------------------------------------------------------------------------------------- +#-- System ------------------------------------------------------------------------------------------------------------ +#---------------------------------------------------------------------------------------------------------------------- + +RUN apt-get update -qq + +#---------------------------------------------------------------------------------------------------------------------- +#-- Android NDK (Android SDK comes from base `android-components` image) ---------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------- + +RUN mkdir -p /build +WORKDIR /build + +# ENV ANDROID_HOME /build/android-sdk +# ENV ANDROID_SDK_HOME /build/android-sdk +ENV ANDROID_NDK_HOME /build/android-ndk +# ENV PATH ${PATH}:${ANDROID_NDK_HOME}/tools:${ANDROID_SDK_HOME}/tools/bin:${ANDROID_SDK_HOME}/platform-tools:/opt/tools:${ANDROID_SDK_HOME}/build-tools/${ANDROID_BUILD_TOOLS} + +RUN curl -L https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux-x86_64.zip > ndk.zip \ + && unzip ndk.zip -d /build \ + && rm ndk.zip \ + && mv /build/android-ndk-${ANDROID_NDK_VERSION} ${ANDROID_NDK_HOME} + +#---------------------------------------------------------------------------------------------------------------------- +#-- Rust (cribbed from https://github.com/rust-lang-nursery/docker-rust/blob/ced83778ec6fea7f63091a484946f95eac0ee611/1.27.1/stretch/Dockerfile) +#-- Rust after the Android NDK since Rust rolls forward more frequently. +#---------------------------------------------------------------------------------------------------------------------- + +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH \ + RUST_VERSION=1.27.1 + +RUN set -eux; \ + rustArch='x86_64-unknown-linux-gnu'; rustupSha256='4d382e77fd6760282912d2d9beec5e260ec919efd3cb9bdb64fe1207e84b9d91'; \ + url="https://static.rust-lang.org/rustup/archive/1.12.0/${rustArch}/rustup-init"; \ + wget "$url"; \ + echo "${rustupSha256} *rustup-init" | sha256sum -c -; \ + chmod +x rustup-init; \ + ./rustup-init -y --no-modify-path --default-toolchain $RUST_VERSION; \ + rm rustup-init; \ + chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ + rustup --version; \ + cargo --version; \ + rustc --version; \ + rustup target add i686-linux-android; \ + rustup target add arm-linux-androideabi; \ + rustup target add aarch64-linux-android + +#---------------------------------------------------------------------------------------------------------------------- +#-- Project ----------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------- + +RUN git clone $PROJECT_REPOSITORY + +WORKDIR /build/mentat + +# Temporary. +RUN git fetch origin build-android-sdk && git checkout origin/build-android-sdk && git show-ref HEAD + +# Populate dependencies. +RUN ./sdks/android/Mentat/gradlew --no-daemon -p sdks/android/Mentat tasks + +# Cache toolchains. +RUN ./sdks/android/Mentat/gradlew --no-daemon -p sdks/android/Mentat generateToolchains + +# Build Rust. +RUN ./sdks/android/Mentat/gradlew --no-daemon -p sdks/android/Mentat cargoBuild + +# Actually build. In the future, we might also test and lint (to cache additional dependencies). +RUN ./sdks/android/Mentat/gradlew --no-daemon -p sdks/android/Mentat assemble + +# Drop built Rust artifacts. +RUN cargo clean + +#---------------------------------------------------------------------------------------------------------------------- +# -- Cleanup ---------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------- + +RUN apt-get clean diff --git a/automation/taskcluster/decision_task_pull_request.py b/automation/taskcluster/decision_task_pull_request.py new file mode 100644 index 00000000..69e4e98c --- /dev/null +++ b/automation/taskcluster/decision_task_pull_request.py @@ -0,0 +1,122 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import datetime +import json +import os +import taskcluster +import re +import subprocess +import sys + + +""" +Decision task for pull requests +""" + +TASK_ID = os.environ.get('TASK_ID') +REPO_URL = os.environ.get('GITHUB_HEAD_REPO_URL') +BRANCH = os.environ.get('GITHUB_HEAD_BRANCH') +COMMIT = os.environ.get('GITHUB_HEAD_SHA') + +def fetch_module_names(): + process = subprocess.Popen(["./gradlew", "--no-daemon", "printModules"], stdout=subprocess.PIPE, + cwd=os.path.join(os.getcwd(), "sdks", "android", "Mentat")) + (output, err) = process.communicate() + exit_code = process.wait() + + if exit_code is not 0: + print "Gradle command returned error:", exit_code + + return re.findall('module: (.*)', output, re.M) + + +def schedule_task(queue, taskId, task): + print "TASK", taskId + print json.dumps(task, indent=4, separators=(',', ': ')) + + result = queue.createTask(taskId, task) + print "RESULT", taskId + print json.dumps(result, indent=4, separators=(',', ': ')) + + +def create_task(name, description, command): + created = datetime.datetime.now() + expires = taskcluster.fromNow('1 year') + deadline = taskcluster.fromNow('1 day') + + return { + "workerType": 'github-worker', + "taskGroupId": TASK_ID, + "expires": taskcluster.stringDate(expires), + "retries": 5, + "created": taskcluster.stringDate(created), + "tags": {}, + "priority": "lowest", + "schedulerId": "taskcluster-github", + "deadline": taskcluster.stringDate(deadline), + "dependencies": [ TASK_ID ], + "routes": [], + "scopes": [], + "requires": "all-completed", + "payload": { + "features": {}, + "maxRunTime": 7200, + "image": "mozillamobile/mentat:1.1", + "command": [ + "/bin/bash", + "--login", + "-cx", + "export TERM=dumb && git fetch %s %s && git config advice.detachedHead false && git checkout %s && cd sdks/android/Mentat && ./gradlew --no-daemon clean %s" % (REPO_URL, BRANCH, COMMIT, command) + ], + "artifacts": {}, + "deadline": taskcluster.stringDate(deadline) + }, + "provisionerId": "aws-provisioner-v1", + "metadata": { + "name": name, + "description": description, + "owner": "nalexander@mozilla.com", + "source": "https://github.com/mozilla/mentat" + } + } + + +def create_module_task(module): + return create_task( + name='Mentat Android SDK - Module ' + module, + description='Building and testing module ' + module, + command=" ".join(map(lambda x: module + ":" + x, ['assemble', 'test', 'lint']))) + + +# def create_detekt_task(): +# return create_task( +# name='Android Components - detekt', +# description='Running detekt over all modules', +# command='detektCheck') + + +# def create_ktlint_task(): +# return create_task( +# name='Android Components - ktlint', +# description='Running ktlint over all modules', +# command='ktlint') + + +if __name__ == "__main__": + queue = taskcluster.Queue({ 'baseUrl': 'http://taskcluster/queue/v1' }) + + modules = fetch_module_names() + + if len(modules) == 0: + print "Could not get module names from gradle" + sys.exit(2) + + for module in modules: + task = create_module_task(module) + task_id = taskcluster.slugId() + schedule_task(queue, task_id, task) + + # schedule_task(queue, taskcluster.slugId(), create_detekt_task()) + # schedule_task(queue, taskcluster.slugId(), create_ktlint_task()) diff --git a/automation/taskcluster/release/fetch-bintray-api-key.py b/automation/taskcluster/release/fetch-bintray-api-key.py new file mode 100644 index 00000000..53bec81b --- /dev/null +++ b/automation/taskcluster/release/fetch-bintray-api-key.py @@ -0,0 +1,28 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +import taskcluster + +SECRET_NAME = 'project/mentat/publish' +TASKCLUSTER_BASE_URL = 'http://taskcluster/secrets/v1' + +def fetch_publish_secrets(secret_name): + """Fetch and return secrets from taskcluster's secret service""" + secrets = taskcluster.Secrets({'baseUrl': TASKCLUSTER_BASE_URL}) + return secrets.get(secret_name) + +def main(): + """Fetch the bintray user and api key from taskcluster's secret service + and save it to local.properties in the project root directory. + """ + data = fetch_publish_secrets(SECRET_NAME) + + properties_file_path = os.path.join(os.path.dirname(__file__), '../../../sdks/android/Mentat/local.properties') + with open(properties_file_path, 'w') as properties_file: + properties_file.write("bintray.user=%s\n" % data['secret']['bintray_user']) + properties_file.write("bintray.apikey=%s\n" % data['secret']['bintray_apikey']) + +if __name__ == "__main__": + main() diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index 98c45d8d..5d2c532e 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -208,21 +208,19 @@ impl<'a, 'c> InProgressTransactResult<'a, 'c> { /// A destructor `store_destroy` is provided for releasing the memory for this /// pointer type. #[no_mangle] -pub extern "C" fn store_open(uri: *const c_char) -> *mut Store { +pub unsafe extern "C" fn store_open(uri: *const c_char, error: *mut ExternError) -> *mut Store { assert_not_null!(uri); let uri = c_char_to_string(uri); - let store = Store::open(&uri).expect("expected a store"); - Box::into_raw(Box::new(store)) + translate_result(Store::open(&uri), error) } /// Variant of store_open that opens an encrypted database. #[cfg(feature = "sqlcipher")] #[no_mangle] -pub extern "C" fn store_open_encrypted(uri: *const c_char, key: *const c_char) -> *mut Store { +pub unsafe extern "C" fn store_open_encrypted(uri: *const c_char, key: *const c_char, error: *mut ExternError) -> *mut Store { let uri = c_char_to_string(uri); let key = c_char_to_string(key); - let store = Store::open_with_key(&uri, &key).expect("expected a store"); - Box::into_raw(Box::new(store)) + translate_result(Store::open_with_key(&uri, &key), error) } // TODO: open empty diff --git a/sdks/android/Mentat/build.gradle b/sdks/android/Mentat/build.gradle index ee3ebc49..22784c91 100644 --- a/sdks/android/Mentat/build.gradle +++ b/sdks/android/Mentat/build.gradle @@ -4,27 +4,32 @@ buildscript { ext.kotlin_version = '1.2.41' ext.library = [ - version: '0.3.1' + version: '0.10.0' ] ext.build = [ - compileSdkVersion: 27, - targetSdkVersion: 27, - minSdkVersion: 19 + compileSdkVersion: 27, + targetSdkVersion: 27, + minSdkVersion: 21, // So that we can publish for aarch64. ] repositories { jcenter() google() + maven { + url "https://plugins.gradle.org/m2/" + } } + dependencies { - classpath 'com.android.tools.build:gradle:3.1.2' + classpath 'com.android.tools.build:gradle:3.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Publish. classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + classpath 'gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.0.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -32,11 +37,19 @@ buildscript { allprojects { repositories { - jcenter() google() + jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } + +task printModules { + doLast { + subprojects.each { p -> + println "module: " + p.path + } + } +} diff --git a/sdks/android/Mentat/gradle.properties b/sdks/android/Mentat/gradle.properties index 89e15d97..30c2374f 100644 --- a/sdks/android/Mentat/gradle.properties +++ b/sdks/android/Mentat/gradle.properties @@ -10,6 +10,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m +org.gradle.configureondemand=false # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit @@ -17,10 +18,11 @@ org.gradle.jvmargs=-Xmx1536m # org.gradle.parallel=true libGroupId=org.mozilla.mentat -libRepositoryName=Mentat +libRepositoryName=mentat libProjectName=mentat libProjectDescription=A persistent, relational store inspired by Datomic and DataScript. libUrl=https://github.com/mozilla/mentat libVcsUrl=https://github.com/mozilla/mentat.git -libLicense=MPL-2.0 +libLicense 'Apache-2.0' +libLicenseUrl 'http://www.apache.org/licenses/LICENSE-2.0.txt' diff --git a/sdks/android/Mentat/gradle/wrapper/gradle-wrapper.properties b/sdks/android/Mentat/gradle/wrapper/gradle-wrapper.properties index 73bb13d5..610ad4c5 100644 --- a/sdks/android/Mentat/gradle/wrapper/gradle-wrapper.properties +++ b/sdks/android/Mentat/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip diff --git a/sdks/android/Mentat/library/build.gradle b/sdks/android/Mentat/library/build.gradle index b8a82ec1..c0f3d9c7 100644 --- a/sdks/android/Mentat/library/build.gradle +++ b/sdks/android/Mentat/library/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.library' +apply plugin: 'org.mozilla.rust-android-gradle.rust-android' + apply plugin: 'kotlin-android' apply plugin: 'com.jfrog.bintray' // Simply applying this plugin gets bintray to publish a pom file. @@ -31,71 +33,56 @@ android { lintOptions { abortOnError false } + + // Help folks debugging by including symbols in our native libraries. Yes, this makes the + // resulting AAR very large. The Android ecosystem seems to be in flux around who is in charge + // of stripping native binaries, but for now let's provide symbols and see how consumers react. + packagingOptions { + doNotStrip "**/*.so" + } +} + +cargo { + module = '../../../../ffi' + targetDirectory = '../../../../target' + targetInclude = 'libmentat_ffi.so' + + targets = [ + 'x86', + 'arm', + 'arm64', + ] } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) androidTestImplementation 'com.android.support:support-annotations:27.1.1' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:rules:1.0.2' - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.android.support.constraint:constraint-layout:1.1.0' - implementation 'com.android.support:design:27.1.1' testImplementation 'junit:junit:4.12' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'net.java.dev.jna:jna:4.5.1' + implementation 'net.java.dev.jna:jna:4.5.2@aar' } + repositories { mavenCentral() } -// Publishing to jcenter/bintray. -def libGroupId = properties.libGroupId -def libRepoName = properties.libRepositoryName -def libProjectName = properties.libProjectName -def libProjectDescription = properties.libProjectDescription -def libUrl = properties.libUrl -def libVcsUrl = properties.libVcsUrl -def libLicense = properties.libLicense - -Properties localProperties = null -if (project.rootProject.file('local.properties').canRead()) { - localProperties = new Properties() - localProperties.load(project.rootProject.file('local.properties').newDataInputStream()) -} - -version = rootProject.ext.library['version'] - -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -artifacts { - archives sourcesJar -} - - -group = libGroupId -archivesBaseName = libProjectName - -bintray { - user = localProperties != null ? localProperties.getProperty("bintray.user") : "" - key = localProperties != null ? localProperties.getProperty("bintray.apikey") : "" - - configurations = ['archives'] - pkg { - repo = libRepoName - name = libProjectName - userOrg = "grisha" // Temporary org name until package is on jcenter. Issue #725. - desc = libProjectDescription - websiteUrl = libUrl - vcsUrl = libVcsUrl - licenses = [libLicense] - publish = true - publicDownloadNumbers = true +afterEvaluate { + // The `cargoBuild` tasks isn't available until after evaluation. + android.libraryVariants.all { variant -> + def productFlavor = "" + variant.productFlavors.each { + productFlavor += "${it.name.capitalize()}" + } + def buildType = "${variant.buildType.name.capitalize()}" + tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"]) } } + +archivesBaseName = 'mentat' + +apply from: '../publish.gradle' +ext.configurePublish( + 'org.mozilla.mentat', + 'mentat', + 'A persistent, embedded knowledge base.') diff --git a/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java b/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java index ac199eb6..0b5be28d 100644 --- a/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java +++ b/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java @@ -71,7 +71,7 @@ public class FFIIntegrationTest { @Test public void openInMemoryStoreSucceeds() throws Exception { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); assertNotNull(mentat); } @@ -79,7 +79,7 @@ public class FFIIntegrationTest { public void openStoreInLocationSucceeds() throws Exception { Context context = InstrumentationRegistry.getTargetContext(); String path = context.getDatabasePath("test.db").getAbsolutePath(); - Mentat mentat = new Mentat(path); + Mentat mentat = Mentat.open(path); assertNotNull(mentat); } @@ -114,7 +114,7 @@ public class FFIIntegrationTest { public Mentat openAndInitializeCitiesStore() { if (this.mentat == null) { - this.mentat = new Mentat(); + this.mentat = Mentat.open(); this.transactCitiesSchema(mentat); this.transactSeattleData(mentat); } @@ -177,7 +177,7 @@ public class FFIIntegrationTest { @Test public void transactingVocabularySucceeds() { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport schemaReport = this.transactCitiesSchema(mentat); assertNotNull(schemaReport); assertTrue(schemaReport.getTxId() > 0); @@ -185,7 +185,7 @@ public class FFIIntegrationTest { @Test public void transactingEntitiesSucceeds() { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); this.transactCitiesSchema(mentat); TxReport dataReport = this.transactSeattleData(mentat); assertNotNull(dataReport); @@ -363,7 +363,7 @@ public class FFIIntegrationTest { @Test public void bindingLongValueSucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?e . :in ?long :where [?e :foo/long ?long]]"; @@ -384,7 +384,7 @@ public class FFIIntegrationTest { @Test public void bindingRefValueSucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; long stringEntid = mentat.entIdForAttribute(":foo/string"); final Long bEntid = report.getEntidForTempId("b"); @@ -406,7 +406,7 @@ public class FFIIntegrationTest { @Test public void bindingRefKwValueSucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; String refKeyword = ":foo/string"; final Long bEntid = report.getEntidForTempId("b"); @@ -428,7 +428,7 @@ public class FFIIntegrationTest { @Test public void bindingKwValueSucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]"; @@ -449,7 +449,7 @@ public class FFIIntegrationTest { @Test public void bindingDateValueSucceeds() throws InterruptedException, ParseException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); @@ -493,7 +493,7 @@ public class FFIIntegrationTest { @Test public void bindingUuidValueSucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]"; @@ -515,7 +515,7 @@ public class FFIIntegrationTest { @Test public void bindingBooleanValueSucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]"; @@ -537,7 +537,7 @@ public class FFIIntegrationTest { @Test public void bindingDoubleValueSucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]"; @@ -558,7 +558,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToLong() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]"; @@ -580,7 +580,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToRef() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?e . :where [?e :foo/long 25]]"; @@ -602,7 +602,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToKeyword() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]"; @@ -624,7 +624,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToBoolean() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]"; @@ -646,7 +646,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToDouble() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]"; @@ -668,7 +668,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToDate() throws InterruptedException, ParseException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]"; @@ -693,7 +693,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToString() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]"; @@ -715,7 +715,7 @@ public class FFIIntegrationTest { @Test public void typedValueConvertsToUUID() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); String query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]"; @@ -738,7 +738,7 @@ public class FFIIntegrationTest { @Test public void valueForAttributeOfEntitySucceeds() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; final Long aEntid = report.getEntidForTempId("a"); TypedValue value = mentat.valueForAttributeOfEntity(":foo/long", aEntid); @@ -748,7 +748,7 @@ public class FFIIntegrationTest { @Test public void entidForAttributeSucceeds() { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); this.populateWithTypesSchema(mentat); long entid = mentat.entIdForAttribute(":foo/long"); assertEquals(65540, entid); @@ -756,7 +756,7 @@ public class FFIIntegrationTest { @Test public void testInProgressTransact() { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; assertNotNull(report); @@ -764,7 +764,7 @@ public class FFIIntegrationTest { @Test public void testInProgressRollback() { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); TxReport report = this.populateWithTypesSchema(mentat).dataReport; assertNotNull(report); long aEntid = report.getEntidForTempId("a"); @@ -782,7 +782,7 @@ public class FFIIntegrationTest { @Test public void testInProgressEntityBuilder() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); DBSetupResult reports = this.populateWithTypesSchema(mentat); long bEntid = reports.dataReport.getEntidForTempId("b"); final long longEntid = reports.schemaReport.getEntidForTempId("l"); @@ -862,7 +862,7 @@ public class FFIIntegrationTest { @Test public void testEntityBuilderForEntid() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); DBSetupResult reports = this.populateWithTypesSchema(mentat); long bEntid = reports.dataReport.getEntidForTempId("b"); final long longEntid = reports.schemaReport.getEntidForTempId("l"); @@ -942,7 +942,7 @@ public class FFIIntegrationTest { @Test public void testEntityBuilderForTempid() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); DBSetupResult reports = this.populateWithTypesSchema(mentat); final long longEntid = reports.schemaReport.getEntidForTempId("l"); @@ -998,7 +998,7 @@ public class FFIIntegrationTest { @Test public void testInProgressBuilderTransact() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); DBSetupResult reports = this.populateWithTypesSchema(mentat); long aEntid = reports.dataReport.getEntidForTempId("a"); long bEntid = reports.dataReport.getEntidForTempId("b"); @@ -1063,7 +1063,7 @@ public class FFIIntegrationTest { @Test public void testEntityBuilderTransact() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); DBSetupResult reports = this.populateWithTypesSchema(mentat); long aEntid = reports.dataReport.getEntidForTempId("a"); long bEntid = reports.dataReport.getEntidForTempId("b"); @@ -1129,7 +1129,7 @@ public class FFIIntegrationTest { @Test public void testEntityBuilderRetract() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); DBSetupResult reports = this.populateWithTypesSchema(mentat); long bEntid = reports.dataReport.getEntidForTempId("b"); final long longEntid = reports.schemaReport.getEntidForTempId("l"); @@ -1199,7 +1199,7 @@ public class FFIIntegrationTest { @Test public void testInProgressBuilderRetract() throws InterruptedException { - Mentat mentat = new Mentat(); + Mentat mentat = Mentat.open(); DBSetupResult reports = this.populateWithTypesSchema(mentat); long bEntid = reports.dataReport.getEntidForTempId("b"); final long longEntid = reports.schemaReport.getEntidForTempId("l"); diff --git a/sdks/android/Mentat/library/src/main/AndroidManifest.xml b/sdks/android/Mentat/library/src/main/AndroidManifest.xml index 51e1ff81..a83569b2 100644 --- a/sdks/android/Mentat/library/src/main/AndroidManifest.xml +++ b/sdks/android/Mentat/library/src/main/AndroidManifest.xml @@ -1,6 +1,4 @@ - - - \ No newline at end of file + diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java index b8ba5d42..44ac12c2 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java @@ -42,7 +42,7 @@ public interface JNA extends Library { class InProgressBuilder extends PointerType {} class EntityBuilder extends PointerType {} - Store store_open(String dbPath); + Store store_open(String dbPath, RustError.ByReference err); void destroy(Pointer obj); void uuid_destroy(Pointer obj); diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java index 94f4f285..f51ecc7f 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java @@ -21,32 +21,39 @@ import com.sun.jna.Pointer; * The raw pointer it holds is a pointer to a Store. */ public class Mentat extends RustObject { - static { System.loadLibrary("mentat_ffi"); } - /** - * Open a connection to a Store in a given location.
- * If the store does not already exist, one will be created. - * @param dbPath The URI as a String of the store to open. - */ - public Mentat(String dbPath) { - this(JNA.INSTANCE.store_open(dbPath)); - } - - /** - * Open a connection to an in-memory Store. - */ - public Mentat() { - this(JNA.INSTANCE.store_open("")); - } - /** * Create a new Mentat with the provided pointer to a Mentat Store * @param rawPointer A pointer to a Mentat Store. */ - public Mentat(JNA.Store rawPointer) { super(rawPointer); } + private Mentat(JNA.Store rawPointer) { super(rawPointer); } + + /** + * Open a connection to an in-memory Mentat Store. + */ + public static Mentat open() { + return open(""); + } + + /** + * Open a connection to a Store in a given location. + *
+ * If the store does not already exist, one will be created. + * @param dbPath The URI as a String of the store to open. + */ + public static Mentat open(String dbPath) { + RustError.ByReference err = new RustError.ByReference(); + JNA.Store store = JNA.INSTANCE.store_open(dbPath, err); + if (!err.isSuccess()) { + err.logAndConsumeError("Mentat"); + throw new RuntimeException("Failed to open store: " + dbPath); + } + + return new Mentat(store); + } /** * Add an attribute to the cache. The {@link CacheDirection} determines how that attribute can be diff --git a/sdks/android/Mentat/library/src/main/jniLibs/arm64/libjnidispatch.so b/sdks/android/Mentat/library/src/main/jniLibs/arm64/libjnidispatch.so deleted file mode 100644 index d2b1a8f2..00000000 Binary files a/sdks/android/Mentat/library/src/main/jniLibs/arm64/libjnidispatch.so and /dev/null differ diff --git a/sdks/android/Mentat/library/src/main/jniLibs/arm64/libmentat_ffi.so b/sdks/android/Mentat/library/src/main/jniLibs/arm64/libmentat_ffi.so deleted file mode 120000 index 192961bd..00000000 --- a/sdks/android/Mentat/library/src/main/jniLibs/arm64/libmentat_ffi.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../target/aarch64-linux-android/release/libmentat_ffi.so \ No newline at end of file diff --git a/sdks/android/Mentat/library/src/main/jniLibs/armeabi/libjnidispatch.so b/sdks/android/Mentat/library/src/main/jniLibs/armeabi/libjnidispatch.so deleted file mode 100644 index bc167135..00000000 Binary files a/sdks/android/Mentat/library/src/main/jniLibs/armeabi/libjnidispatch.so and /dev/null differ diff --git a/sdks/android/Mentat/library/src/main/jniLibs/armeabi/libmentat_ffi.so b/sdks/android/Mentat/library/src/main/jniLibs/armeabi/libmentat_ffi.so deleted file mode 120000 index f248755f..00000000 --- a/sdks/android/Mentat/library/src/main/jniLibs/armeabi/libmentat_ffi.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../target/armv7-linux-androideabi/release/libmentat_ffi.so \ No newline at end of file diff --git a/sdks/android/Mentat/library/src/main/jniLibs/x86/libjnidispatch.so b/sdks/android/Mentat/library/src/main/jniLibs/x86/libjnidispatch.so deleted file mode 100644 index a87d0865..00000000 Binary files a/sdks/android/Mentat/library/src/main/jniLibs/x86/libjnidispatch.so and /dev/null differ diff --git a/sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so b/sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so deleted file mode 120000 index 2d4f93c2..00000000 --- a/sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../target/i686-linux-android/release/libmentat_ffi.so \ No newline at end of file diff --git a/sdks/android/Mentat/library/src/main/res/values/strings.xml b/sdks/android/Mentat/library/src/main/res/values/strings.xml deleted file mode 100644 index 30b035ae..00000000 --- a/sdks/android/Mentat/library/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Mentat - diff --git a/sdks/android/Mentat/publish.gradle b/sdks/android/Mentat/publish.gradle new file mode 100644 index 00000000..43504583 --- /dev/null +++ b/sdks/android/Mentat/publish.gradle @@ -0,0 +1,99 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +def libRepoName = properties.libRepositoryName +def libUrl = properties.libUrl +def libVcsUrl = properties.libVcsUrl +def libLicense = properties.libLicense +def libLicenseUrl = properties.libLicenseUrl + +ext.configurePublish = { groupIdArg, artifactIdArg, descriptionArg -> + apply plugin: 'com.github.dcendents.android-maven' + + group = groupIdArg + + install { + repositories.mavenInstaller { + pom { + project { + packaging 'aar' + groupId groupIdArg + artifactId artifactIdArg + + name libRepoName + description descriptionArg + url libUrl + + licenses { + license { + name libLicense + url libLicenseUrl + } + } + + developers { + developer { + id 'nalexander' + name 'Nick Alexander' + email 'nalexander@mozilla.com' + } + } + + scm { + connection libVcsUrl + developerConnection libVcsUrl + url libUrl + } + } + } + } + } + + apply plugin: 'com.jfrog.bintray' + + version = rootProject.ext.library['version'] + + task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' + } + + task javadoc(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + } + + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir + } + + artifacts { + //archives javadocJar + archives sourcesJar + } + + Properties localProperties = null; + if (project.rootProject.file('local.properties').canRead()) { + localProperties = new Properties() + localProperties.load(project.rootProject.file('local.properties').newDataInputStream()) + } + + bintray { + user = localProperties != null ? localProperties.getProperty("bintray.user") : "" + key = localProperties != null ? localProperties.getProperty("bintray.apikey") : "" + + configurations = ['archives'] + pkg { + repo = libRepoName + name = artifactIdArg + desc = descriptionArg + websiteUrl = libUrl + vcsUrl = libVcsUrl + licenses = [libLicense] + publish = true + publicDownloadNumbers = true + } + } +} diff --git a/sdks/android/Mentat/samples/basic/build.gradle b/sdks/android/Mentat/samples/basic/build.gradle index 49fa1822..3c7808a2 100644 --- a/sdks/android/Mentat/samples/basic/build.gradle +++ b/sdks/android/Mentat/samples/basic/build.gradle @@ -31,6 +31,8 @@ dependencies { implementation project(':library') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "com.android.support:appcompat-v7:27.1.1" + implementation 'com.android.support.constraint:constraint-layout:1.1.0' + implementation 'com.android.support:design:27.1.1' } repositories { diff --git a/sdks/android/Mentat/samples/basic/src/main/java/org/mozilla/samples/basic/MainActivity.kt b/sdks/android/Mentat/samples/basic/src/main/java/org/mozilla/samples/basic/MainActivity.kt index 81c75a42..e7627b4d 100644 --- a/sdks/android/Mentat/samples/basic/src/main/java/org/mozilla/samples/basic/MainActivity.kt +++ b/sdks/android/Mentat/samples/basic/src/main/java/org/mozilla/samples/basic/MainActivity.kt @@ -21,8 +21,9 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - Mentat(this.getDatabasePath("test.db").absolutePath).use { + this.getDatabasePath("test.db").absoluteFile.parentFile.mkdirs(); + + Mentat.open(this.getDatabasePath("test.db").absolutePath).use { } } } - diff --git a/sdks/swift/Mentat/Mentat/Mentat.swift b/sdks/swift/Mentat/Mentat/Mentat.swift index 2bd569e6..712bcf4b 100644 --- a/sdks/swift/Mentat/Mentat/Mentat.swift +++ b/sdks/swift/Mentat/Mentat/Mentat.swift @@ -58,8 +58,8 @@ open class Mentat: RustObject { - Parameter storeURI: The URI as a String of the store to open. If no store URI is provided, an in-memory store will be opened. */ - public convenience init(storeURI: String = "") { - self.init(raw: store_open(storeURI)) + public class func open(storeURI: String = "") throws -> Mentat { + return Mentat(raw: try RustError.unwrap({err in store_open(storeURI, err) })) } /** diff --git a/sdks/swift/Mentat/Mentat/store.h b/sdks/swift/Mentat/Mentat/store.h index 4163358b..729875f6 100644 --- a/sdks/swift/Mentat/Mentat/store.h +++ b/sdks/swift/Mentat/Mentat/store.h @@ -102,7 +102,7 @@ typedef NS_ENUM(NSInteger, ValueType) { }; // Store -struct Store*_Nonnull store_open(const char*_Nonnull uri); +struct Store*_Nonnull store_open(const char*_Nonnull uri, struct RustError* _Nonnull error); // Destructors. void destroy(void* _Nullable obj); diff --git a/src/store.rs b/src/store.rs index de86f6da..314e0a48 100644 --- a/src/store.rs +++ b/src/store.rs @@ -41,8 +41,6 @@ use mentat_db::{ #[cfg(feature = "syncable")] use mentat_tolstoy::Syncer; -use uuid::Uuid; - use conn::{ CacheAction, CacheDirection, @@ -51,7 +49,11 @@ use conn::{ InProgressRead, Pullable, Queryable, - Syncable +}; + +#[cfg(feature = "syncable")] +use conn::{ + Syncable, }; use errors::*; @@ -238,6 +240,9 @@ impl Pullable for Store { } } +#[cfg(feature = "syncable")] +use uuid::Uuid; + #[cfg(feature = "syncable")] impl Syncable for Store { fn sync(&mut self, server_uri: &String, user_uuid: &String) -> Result<()> { @@ -266,6 +271,8 @@ mod tests { Duration, }; + use uuid::Uuid; + use mentat_db::cache::{ SQLiteAttributeCache, };