[build] - initial commit
This commit is contained in:
56
scripts/exclude-activity-gen.json
Normal file
56
scripts/exclude-activity-gen.json
Normal file
@@ -0,0 +1,56 @@
|
||||
[
|
||||
"BaseLocationActivity",
|
||||
"MapSnapshotterMarkerActivity",
|
||||
"MapSnapshotterReuseActivity",
|
||||
"MapSnapshotterLocalStyleActivity",
|
||||
"LatLngBoundsActivity",
|
||||
"BottomSheetActivity",
|
||||
"MapSnapshotterActivity",
|
||||
"MockLocationEngine",
|
||||
"DeleteRegionActivity",
|
||||
"RealTimeGeoJsonActivity",
|
||||
"UpdateMetadataActivity",
|
||||
"CarDrivingActivity",
|
||||
"MyLocationTrackingModeActivity",
|
||||
"MyLocationToggleActivity",
|
||||
"MyLocationTintActivity",
|
||||
"MyLocationDrawableActivity",
|
||||
"DoubleMapActivity",
|
||||
"LocationPickerActivity",
|
||||
"GeoJsonClusteringActivity",
|
||||
"RuntimeStyleTestActivity",
|
||||
"AnimatedSymbolLayerActivity",
|
||||
"ViewPagerActivity",
|
||||
"MapFragmentActivity",
|
||||
"SupportMapFragmentActivity",
|
||||
"SnapshotActivity",
|
||||
"NavigationDrawerActivity",
|
||||
"QueryRenderedFeaturesBoxHighlightActivity",
|
||||
"MultiMapActivity",
|
||||
"MapInDialogActivity",
|
||||
"ManualZoomActivity",
|
||||
"MaxMinZoomActivity",
|
||||
"ScrollByActivity",
|
||||
"ZoomFunctionSymbolLayerActivity",
|
||||
"SymbolGeneratorActivity",
|
||||
"TextureViewTransparentBackgroundActivity",
|
||||
"SimpleMapActivity",
|
||||
"RenderTestActivity",
|
||||
"SymbolLayerActivity",
|
||||
"LocationFragmentActivity",
|
||||
"MergeOfflineRegionsActivity",
|
||||
"NestedViewPagerActivity",
|
||||
"DebugModeActivity",
|
||||
"TextureViewDebugModeActivity",
|
||||
"RuntimeStyleActivity",
|
||||
"GridSourceActivity",
|
||||
"AnimatedImageSourceActivity",
|
||||
"EspressoTestActivity",
|
||||
"ChangeResourcesCachePathActivity",
|
||||
"EspressoTestActivity",
|
||||
"FragmentBackStackActivity",
|
||||
"ChildFragmentMapInDialogActivity",
|
||||
"PerformanceMeasurementActivity",
|
||||
"DownloadRegionActivity",
|
||||
"CacheManagementActivity"
|
||||
]
|
||||
39
scripts/generate-license.py
Normal file
39
scripts/generate-license.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
path = os.getcwd() + "/platform/android/"
|
||||
with open(path + "LICENSE.md", 'w') as licenseFile:
|
||||
licenseFile.write("<!-- This file was generated. Use `make android-license` to update. --> \n")
|
||||
licenseFile.write("## Additional Mapbox GL licenses \n")
|
||||
with open(path + "MapboxGLAndroidSDK/build/reports/licenses/licenseReleaseReport.json", 'r') as dataFile:
|
||||
data = json.load(dataFile)
|
||||
|
||||
gradleLicensePlugin ="""
|
||||
{
|
||||
"project": "Gradle License Plugin",
|
||||
"url": "https://github.com/jaredsburrows/gradle-license-plugin",
|
||||
"licenses": [
|
||||
{
|
||||
"license": "The Apache Software License, Version 2.0",
|
||||
"license_url": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
data.append(json.loads(gradleLicensePlugin))
|
||||
|
||||
licenseName = ""
|
||||
licenseUrl = ""
|
||||
for entry in data:
|
||||
projectName = entry["project"]
|
||||
projectUrl = entry["url"]
|
||||
for license in entry["licenses"]:
|
||||
licenseName = license["license"]
|
||||
licenseUrl = license["license_url"]
|
||||
|
||||
licenseFile.write("Mapbox GL uses portions of the %s. \n" % projectName +
|
||||
("URL: [%s](%s) \n" % (projectUrl, projectUrl) if projectUrl is not None else "") +
|
||||
"License: [%s](%s)" % (licenseName, licenseUrl) +
|
||||
"\n\n===========================================================================\n\n")
|
||||
394
scripts/generate-style-code.js
Executable file
394
scripts/generate-style-code.js
Executable file
@@ -0,0 +1,394 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const ejs = require('ejs');
|
||||
const spec = require('../../../scripts/style-spec');
|
||||
const _ = require('lodash');
|
||||
|
||||
// FIXME: https://github.com/mapbox/mapbox-gl-native/issues/15008
|
||||
delete spec.layout_circle["circle-sort-key"]
|
||||
delete spec.layout_line["line-sort-key"]
|
||||
delete spec.layout_fill["fill-sort-key"]
|
||||
|
||||
require('../../../scripts/style-code');
|
||||
|
||||
// Specification parsing //
|
||||
const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => {
|
||||
var property = spec[`light`][name];
|
||||
property.name = name;
|
||||
property['light-property'] = true;
|
||||
property.doc = property.doc.replace(/°/g,'°');
|
||||
memo.push(property);
|
||||
return memo;
|
||||
}, []);
|
||||
|
||||
// Collect layer types from spec
|
||||
var layers = Object.keys(spec.layer.type.values).map((type) => {
|
||||
const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
|
||||
if (name !== 'visibility') {
|
||||
spec[`layout_${type}`][name].name = name;
|
||||
memo.push(spec[`layout_${type}`][name]);
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
|
||||
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
|
||||
spec[`paint_${type}`][name].name = name;
|
||||
memo.push(spec[`paint_${type}`][name]);
|
||||
return memo;
|
||||
}, []);
|
||||
|
||||
return {
|
||||
type: type,
|
||||
doc: spec.layer.type.values[type].doc,
|
||||
layoutProperties: layoutProperties,
|
||||
paintProperties: paintProperties,
|
||||
properties: layoutProperties.concat(paintProperties)
|
||||
};
|
||||
});
|
||||
|
||||
// Process all layer properties
|
||||
const uniqueArrayEnum = (prop, enums) => {
|
||||
if (prop.value !== 'enum') return false;
|
||||
const enumsEqual = (val1, val2) => val1.length === val1.length && val1.every((val, i) => val === val2[i]);
|
||||
return enums.filter(e => enumsEqual(Object.keys(prop.values).sort(), Object.keys(e.values).sort())).length == 0;
|
||||
};
|
||||
|
||||
const layoutProperties = _(layers).map('layoutProperties').flatten().value();
|
||||
const paintProperties = _(layers).map('paintProperties').flatten().value();
|
||||
const allProperties = _(layoutProperties).union(paintProperties).union(lightProperties).value();
|
||||
let allEnumProperties = _(allProperties).filter({'type': 'enum'}).value();
|
||||
const uniqueArrayEnumProperties = _(allProperties).filter({'type': 'array'}).filter(prop => uniqueArrayEnum(prop, allEnumProperties)).value();
|
||||
const enumProperties = _(allEnumProperties).union(uniqueArrayEnumProperties).value();
|
||||
|
||||
|
||||
global.propertyType = function propertyType(property) {
|
||||
switch (property.type) {
|
||||
case 'boolean':
|
||||
return 'Boolean';
|
||||
case 'number':
|
||||
return 'Float';
|
||||
case 'formatted':
|
||||
return 'Formatted';
|
||||
case 'string':
|
||||
return 'String';
|
||||
case 'enum':
|
||||
return 'String';
|
||||
case 'color':
|
||||
return 'String';
|
||||
case 'array':
|
||||
return `${propertyType({type:property.value})}[]`;
|
||||
default:
|
||||
throw new Error(`unknown type for ${property.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
global.propertyJavaType = function propertyType(property) {
|
||||
switch (property.type) {
|
||||
case 'boolean':
|
||||
return 'boolean';
|
||||
case 'number':
|
||||
return 'float';
|
||||
case 'formatted':
|
||||
return 'Formatted';
|
||||
case 'string':
|
||||
return 'String';
|
||||
case 'enum':
|
||||
return 'String';
|
||||
case 'color':
|
||||
return 'String';
|
||||
case 'array':
|
||||
return `${propertyJavaType({type:property.value})}[]`;
|
||||
default:
|
||||
throw new Error(`unknown type for ${property.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
global.propertyJNIType = function propertyJNIType(property) {
|
||||
switch (property.type) {
|
||||
case 'boolean':
|
||||
return 'jboolean';
|
||||
case 'number':
|
||||
return 'jfloat';
|
||||
case 'String':
|
||||
return 'String';
|
||||
case 'enum':
|
||||
return 'String';
|
||||
case 'color':
|
||||
return 'String';
|
||||
case 'array':
|
||||
return `jarray<${propertyType({type:property.value})}[]>`;
|
||||
default:
|
||||
return 'jobject*';
|
||||
}
|
||||
}
|
||||
|
||||
global.propertyNativeType = function (property) {
|
||||
if (/-translate-anchor$/.test(property.name)) {
|
||||
return 'TranslateAnchorType';
|
||||
}
|
||||
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
|
||||
return 'AlignmentType';
|
||||
}
|
||||
if (/^(text|icon)-anchor$/.test(property.name)) {
|
||||
return 'SymbolAnchorType';
|
||||
}
|
||||
switch (property.type) {
|
||||
case 'boolean':
|
||||
return 'bool';
|
||||
case 'number':
|
||||
return 'float';
|
||||
case 'formatted':
|
||||
case 'string':
|
||||
return 'std::string';
|
||||
case 'enum':
|
||||
if(property['light-property']){
|
||||
return `Light${camelize(property.name)}Type`;
|
||||
}
|
||||
return `${camelize(property.name)}Type`;
|
||||
case 'color':
|
||||
return `Color`;
|
||||
case 'array':
|
||||
if (property.length) {
|
||||
return `std::array<${propertyType({type: property.value})}, ${property.length}>`;
|
||||
} else {
|
||||
return `std::vector<${propertyType({type: property.value})}>`;
|
||||
}
|
||||
default: throw new Error(`unknown type for ${property.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
global.propertyTypeAnnotation = function propertyTypeAnnotation(property) {
|
||||
switch (property.type) {
|
||||
case 'enum':
|
||||
return `@Property.${snakeCaseUpper(property.name)}`;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
global.defaultExpressionJava = function(property) {
|
||||
switch (property.type) {
|
||||
case 'boolean':
|
||||
return 'boolean';
|
||||
case 'number':
|
||||
return 'number';
|
||||
case 'formatted':
|
||||
return 'format';
|
||||
case 'string':
|
||||
return "string";
|
||||
case 'enum':
|
||||
return "string";
|
||||
case 'color':
|
||||
return 'toColor';
|
||||
case 'array':
|
||||
return "array";
|
||||
default: return "string";
|
||||
}
|
||||
}
|
||||
|
||||
global.defaultValueJava = function(property) {
|
||||
if(property.name.endsWith("-pattern")) {
|
||||
return '"pedestrian-polygon"';
|
||||
}
|
||||
if(property.name.endsWith("-font")) {
|
||||
return 'new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}';
|
||||
}
|
||||
switch (property.type) {
|
||||
case 'boolean':
|
||||
return 'true';
|
||||
case 'number':
|
||||
return '0.3f';
|
||||
case 'formatted':
|
||||
return 'new Formatted(new FormattedSection("default"))'
|
||||
case 'string':
|
||||
return '"' + property['default'] + '"';
|
||||
case 'enum':
|
||||
return snakeCaseUpper(property.name) + "_" + snakeCaseUpper(Object.keys(property.values)[0]);
|
||||
case 'color':
|
||||
return '"rgba(255,128,0,0.7)"';
|
||||
case 'array':
|
||||
switch (property.value) {
|
||||
case 'string':
|
||||
case 'enum':
|
||||
if (property['default'] !== undefined) {
|
||||
return '[' + property['default'] + ']';
|
||||
} else {
|
||||
return 'new String[0]';
|
||||
}
|
||||
case 'number':
|
||||
var result ='new Float[] {';
|
||||
for (var i = 0; i < property.length; i++) {
|
||||
result += "0f";
|
||||
if (i +1 != property.length) {
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
return result + "}";
|
||||
}
|
||||
default: throw new Error(`unknown type for ${property.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces documentation for property factory methods
|
||||
*/
|
||||
global.propertyFactoryMethodDoc = function (property) {
|
||||
var replaceIfPixels = function (doc) {
|
||||
return doc.replace('pixels', 'density-independent pixels')
|
||||
}
|
||||
let doc = replaceIfPixels(property.doc);
|
||||
// Match other items in back ticks
|
||||
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
|
||||
if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0] && _(enumProperties).filter({'name': symbol}).value().length > 0) {
|
||||
// Property 'enums'
|
||||
symbol = snakeCaseUpper(symbol);
|
||||
return '{@link Property.' + symbol + '}';
|
||||
} else if( _(allProperties).filter({'name': symbol}).value().length > 0) {
|
||||
// Other properties
|
||||
return '{@link PropertyFactory#' + camelizeWithLeadingLowercase(symbol) + '}';
|
||||
} else {
|
||||
// Left overs
|
||||
return '`' + symbol + '`';
|
||||
}
|
||||
});
|
||||
return doc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Produces documentation for property value constants
|
||||
*/
|
||||
global.propertyValueDoc = function (property, value) {
|
||||
|
||||
// Match references to other property names & values.
|
||||
// Requires the format 'When `foo` is set to `bar`,'.
|
||||
let doc = property.values[value].doc.replace(/When `(.+?)` is set to `(.+?)`(?: or `([^`]+?)`)?,/g, function (m, peerPropertyName, propertyValue, secondPropertyValue, offset, str) {
|
||||
let otherProperty = snakeCaseUpper(peerPropertyName);
|
||||
let otherValue = snakeCaseUpper(peerPropertyName) + '_' + snakeCaseUpper(propertyValue);
|
||||
const firstPropertyValue = 'When {@link ' + `${otherProperty}` + '} is set to {@link Property#' + `${otherValue}` + '}';
|
||||
if (secondPropertyValue) {
|
||||
return firstPropertyValue + ` or {@link Property#${snakeCaseUpper(peerPropertyName) + '_' + snakeCaseUpper(secondPropertyValue)}},`;
|
||||
} else {
|
||||
return firstPropertyValue + ',';
|
||||
}
|
||||
});
|
||||
|
||||
// Match references to our own property values.
|
||||
// Requires the format 'is equivalent to `bar`'.
|
||||
doc = doc.replace(/is equivalent to `(.+?)`/g, function(m, propertyValue, offset, str) {
|
||||
propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(propertyValue);
|
||||
return 'is equivalent to {@link Property#' + propertyValue + '}';
|
||||
});
|
||||
|
||||
// Match other items in back ticks
|
||||
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
|
||||
if ('values' in property && Object.keys(property.values).indexOf(symbol) !== -1) {
|
||||
// Property values
|
||||
propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(symbol);
|
||||
console.log("Transforming", symbol, propertyValue);
|
||||
return '{@link Property#' + `${propertyValue}` + '}';
|
||||
} else if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0]) {
|
||||
// Property 'enums'
|
||||
if (symbol === 'symbol-sort-key') {
|
||||
return 'symbol sort key';
|
||||
}
|
||||
|
||||
symbol = snakeCaseUpper(symbol);
|
||||
return '{@link ' + symbol + '}';
|
||||
} else {
|
||||
// Left overs
|
||||
return symbol
|
||||
}
|
||||
});
|
||||
return doc;
|
||||
};
|
||||
|
||||
global.isLightProperty = function (property) {
|
||||
return property['light-property'] === true;
|
||||
};
|
||||
|
||||
global.propertyValueType = function (property) {
|
||||
switch (property['property-type']) {
|
||||
default:
|
||||
return `PropertyValue<${evaluatedType(property)}>`;
|
||||
}
|
||||
};
|
||||
|
||||
global.evaluatedType = function (property) {
|
||||
if (/-translate-anchor$/.test(property.name)) {
|
||||
return 'TranslateAnchorType';
|
||||
}
|
||||
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
|
||||
return 'AlignmentType';
|
||||
}
|
||||
if (/^(text|icon)-anchor$/.test(property.name)) {
|
||||
return 'SymbolAnchorType';
|
||||
}
|
||||
if (/position/.test(property.name)) {
|
||||
return 'Position';
|
||||
}
|
||||
switch (property.type) {
|
||||
case 'boolean':
|
||||
return 'bool';
|
||||
case 'number':
|
||||
return 'float';
|
||||
case 'formatted':
|
||||
case 'string':
|
||||
return 'std::string';
|
||||
case 'enum':
|
||||
return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`;
|
||||
case 'color':
|
||||
return `Color`;
|
||||
case 'array':
|
||||
if (property.length) {
|
||||
return `std::array<${evaluatedType({type: property.value})}, ${property.length}>`;
|
||||
} else {
|
||||
return `std::vector<${evaluatedType({type: property.value})}>`;
|
||||
}
|
||||
default: throw new Error(`unknown type for ${property.name}`)
|
||||
}
|
||||
};
|
||||
|
||||
global.supportsZoomFunction = function (property) {
|
||||
return property.expression && property.expression.parameters.indexOf('zoom') > -1;
|
||||
};
|
||||
|
||||
global.supportsPropertyFunction = function (property) {
|
||||
return property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven';
|
||||
};
|
||||
|
||||
// Template processing //
|
||||
|
||||
// Java + JNI Light (Peer model)
|
||||
const lightJava = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs', 'utf8'), {strict: true});
|
||||
const lightJavaUnitTests = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs', 'utf8'), {strict: true});
|
||||
writeIfModified(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java`, lightJava({properties: lightProperties}));
|
||||
writeIfModified(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java`, lightJavaUnitTests({properties: lightProperties}));
|
||||
|
||||
// Java + JNI Layers (Peer model)
|
||||
const layerHpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.hpp.ejs', 'utf8'), {strict: true});
|
||||
const layerCpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.cpp.ejs', 'utf8'), {strict: true});
|
||||
const layerJava = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs', 'utf8'), {strict: true});
|
||||
const layerJavaUnitTests = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs', 'utf8'), {strict: true});
|
||||
|
||||
for (const layer of layers) {
|
||||
writeIfModified(`platform/android/src/style/layers/${layer.type.replace('-', '_')}_layer.hpp`, layerHpp(layer));
|
||||
writeIfModified(`platform/android/src/style/layers/${layer.type.replace('-', '_')}_layer.cpp`, layerCpp(layer));
|
||||
writeIfModified(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/${camelize(layer.type)}Layer.java`, layerJava(layer));
|
||||
writeIfModified(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/${camelize(layer.type)}LayerTest.java`, layerJavaUnitTests(layer));
|
||||
}
|
||||
|
||||
// Java PropertyFactory
|
||||
const propertiesTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs', 'utf8'), {strict: true});
|
||||
writeIfModified(
|
||||
`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java`,
|
||||
propertiesTemplate({layoutProperties: layoutProperties, paintProperties: paintProperties})
|
||||
);
|
||||
|
||||
// Java Property
|
||||
const enumPropertyJavaTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs', 'utf8'), {strict: true});
|
||||
writeIfModified(
|
||||
`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java`,
|
||||
enumPropertyJavaTemplate({properties: enumProperties})
|
||||
);
|
||||
73
scripts/generate-test-code.js
Normal file
73
scripts/generate-test-code.js
Normal file
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const ejs = require('ejs');
|
||||
|
||||
global.iff = function (condition, val) {
|
||||
return condition() ? val : "";
|
||||
}
|
||||
|
||||
global.camelize = function (str) {
|
||||
return str.replace(/(?:^|-)(.)/g, function (_, x) {
|
||||
return x.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
const excludeClasses = JSON.parse(fs.readFileSync('platform/android/scripts/exclude-activity-gen.json', 'utf8'));
|
||||
const appBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity';
|
||||
const testBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen';
|
||||
const subPackages = fs.readdirSync(appBasePath);
|
||||
const ejsConversionTask = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/activity.junit.ejs', 'utf8'), {strict: true});
|
||||
|
||||
if (!fs.existsSync(testBasePath)){
|
||||
fs.mkdirSync(testBasePath);
|
||||
}
|
||||
|
||||
console.log("\nGenerating test activities:\n");
|
||||
var generatedClasses = [];
|
||||
var excludedClasses = [];
|
||||
for(const subPackage of subPackages) {
|
||||
if(!(subPackage.slice(-5) == '.java')) {
|
||||
const activities = fs.readdirSync(appBasePath+'/'+subPackage);
|
||||
|
||||
// create directories for package
|
||||
if (!fs.existsSync(testBasePath+"/"+subPackage)){
|
||||
fs.mkdirSync(testBasePath+"/"+subPackage);
|
||||
}
|
||||
|
||||
for (const activity of activities) {
|
||||
var activityName;
|
||||
if (activity.slice(-5) === '.java') {
|
||||
// .java file
|
||||
activityName = activity.slice(0, -5);
|
||||
} else {
|
||||
// .kt file
|
||||
activityName = activity.slice(0, -3);
|
||||
}
|
||||
|
||||
// create path for test file
|
||||
const filePath = testBasePath+"/"+subPackage+"/"+activityName+'Test.java';
|
||||
|
||||
// try removing previous generated files
|
||||
try {
|
||||
fs.accessSync(filePath, fs.F_OK);
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
// only generate test file if not part of exclude list + if contains Activity in name
|
||||
if ((!(excludeClasses.indexOf(activityName) > -1)) && activityName.includes("Activity")) {
|
||||
fs.writeFileSync(filePath, ejsConversionTask([activityName, subPackage]));
|
||||
generatedClasses.push(activityName);
|
||||
}else{
|
||||
excludedClasses.push(activityName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(const generatedClass of generatedClasses){
|
||||
console.log(generatedClass+"Test");
|
||||
}
|
||||
|
||||
console.log("\nFinished generating " + generatedClasses.length + " activity sanity tests, excluded " + excludeClasses.length + " classes.\n");
|
||||
19
scripts/metrics.sh
Executable file
19
scripts/metrics.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# Track individual architectures
|
||||
scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/armeabi-v7a/libmapbox-gl.so" "Android arm-v7"
|
||||
scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/arm64-v8a/libmapbox-gl.so" "Android arm-v8"
|
||||
scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/x86/libmapbox-gl.so" "Android x86"
|
||||
scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/x86_64/libmapbox-gl.so" "Android x86_64"
|
||||
|
||||
# Track overall library size
|
||||
scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/outputs/aar/MapboxGLAndroidSDK-release.aar" "Android AAR"
|
||||
|
||||
if [[ $CIRCLE_BRANCH == master ]]; then
|
||||
# Build source data for http://mapbox.github.io/mapbox-gl-native/metrics/binary-size/
|
||||
# and log binary sizes to metrics warehouse
|
||||
scripts/publish_binary_size.js
|
||||
fi
|
||||
31
scripts/parse-jacoco-report.py
Executable file
31
scripts/parse-jacoco-report.py
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import re
|
||||
from io import open
|
||||
|
||||
reportPath = os.getcwd() + "/platform/android/MapboxGLAndroidSDK/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml"
|
||||
with open(reportPath, 'r', encoding='utf-8') as jacocoReport:
|
||||
line = jacocoReport.readline().strip()
|
||||
|
||||
# find the last INSTRUCTION coverage report which is a sum of all separate ones
|
||||
instructionIndex = line.rfind('type="INSTRUCTION"')
|
||||
startIndex = line.find('missed', instructionIndex)
|
||||
endIndex = line.find('/>', startIndex)
|
||||
|
||||
# find the missed and covered lines counts
|
||||
numbers = re.match(r'missed="(\d+)" covered="(\d+)"', line[startIndex:endIndex])
|
||||
missed = int(numbers.group(1))
|
||||
covered = int(numbers.group(2))
|
||||
|
||||
# calculate the code coverage percentage
|
||||
percentage = round(covered * 100.0 / (missed + covered), 2)
|
||||
print("Android tests code coverage: %s%%" % (str(percentage)))
|
||||
|
||||
# invoke the script that send the data to s3
|
||||
testEnvironment = "LOCAL"
|
||||
if os.environ.get('IS_LOCAL_DEVELOPMENT').lower()=='false':
|
||||
testEnvironment = "CI"
|
||||
|
||||
cmd = os.getcwd() + ("/scripts/code-coverage.sh %.2f %s %s" % (percentage, "Android", testEnvironment))
|
||||
os.system(cmd)
|
||||
236
scripts/release.py
Normal file
236
scripts/release.py
Normal file
@@ -0,0 +1,236 @@
|
||||
'''
|
||||
Utility to schedule SDK builds on CircleCI.
|
||||
|
||||
Examples:
|
||||
|
||||
- Publish a snapshot from master (release.py uses the current branch)
|
||||
|
||||
$ git branch
|
||||
* master
|
||||
1234-fix-crash
|
||||
$ python release.py --stage snapshot
|
||||
|
||||
- Publish a snapshot from a feature branch (same as before, just switch branchs with git):
|
||||
|
||||
$ git branch
|
||||
master
|
||||
* 1234-fix-crash
|
||||
$ python release.py --stage snapshot
|
||||
|
||||
- Publish a beta from a pre-release branch:
|
||||
|
||||
$ git branch
|
||||
master
|
||||
* release-android-420-beta1
|
||||
$ python release.py --stage beta --version 4.2.0-beta.1
|
||||
|
||||
- Publish a beta from a release branch:
|
||||
|
||||
$ git branch
|
||||
master
|
||||
* release-android-420
|
||||
$ python release.py --stage final --version 4.2.0
|
||||
|
||||
TODO:
|
||||
|
||||
- Add a flag to wait until the release has been built (CircleCI) and published (Maven).
|
||||
|
||||
'''
|
||||
|
||||
import click
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
import subprocess
|
||||
|
||||
# Three stages, from less stable to more stable
|
||||
ALLOWED_STAGES = ['snapshot', 'beta', 'final']
|
||||
|
||||
# Get the version from GRADLE_PROPERTIES_PATH below
|
||||
CURRENT_VERSION_TAG = 'current'
|
||||
|
||||
# You can add your API token on https://circleci.com/account/api
|
||||
CIRCLECI_API_TOKEN_ENV_VAR = 'CIRCLECI_API_TOKEN'
|
||||
|
||||
# In the future we might want to consider alpha, or rc.
|
||||
ALLOWED_PRE_RELEASE = ['beta']
|
||||
|
||||
# We get the default version from here
|
||||
MAPBOX_GL_ANDROID_SDK_PATH = '../MapboxGLAndroidSDK'
|
||||
GRADLE_PROPERTIES_PATH = '%s/gradle.properties' % MAPBOX_GL_ANDROID_SDK_PATH
|
||||
GRADLE_TOKEN = 'VERSION_NAME='
|
||||
|
||||
# Triggers a new build, returns a summary of the build
|
||||
URL_CIRCLECI = 'https://circleci.com/api/v1.1/project/github/mapbox/mapbox-gl-native/tree/' # + :branch
|
||||
|
||||
# We support three parameters: stage, branch, and version
|
||||
@click.command()
|
||||
@click.option('--stage', default=ALLOWED_STAGES[0], type=click.Choice(ALLOWED_STAGES), prompt='Set stage', help='The release stage.')
|
||||
@click.option('--version', default=CURRENT_VERSION_TAG, prompt='Set version', help='The version you want to publish. E.g: 4.2.0-SNAPSHOT, 4.2.0-beta.1, or 4.2.0. If you set the version to "%s", the script will default to the current SNAPSHOT version.' % CURRENT_VERSION_TAG)
|
||||
def release(stage, version):
|
||||
# Validate params
|
||||
final_stage = validate_stage(stage=stage)
|
||||
final_branch = validate_branch(stage=final_stage)
|
||||
final_version = validate_version(stage=final_stage, branch=final_branch, version=version)
|
||||
|
||||
# Get user confirmation
|
||||
click.echo('\n===== Build information =====')
|
||||
click.echo('- Stage: %s' % final_stage)
|
||||
click.echo('- Branch: %s' % final_branch)
|
||||
click.echo('- Version: %s' % final_version)
|
||||
click.confirm('\nDoes it look right?', abort=True)
|
||||
|
||||
# Proceed
|
||||
if (final_stage == 'snapshot'):
|
||||
publish_snapshot(branch=final_branch, version=final_version)
|
||||
elif (final_stage == 'beta'):
|
||||
publish_beta(branch=final_branch, version=final_version)
|
||||
elif (final_stage == 'final'):
|
||||
publish_final(branch=final_branch, version=final_version)
|
||||
|
||||
def validate_stage(stage):
|
||||
if stage not in ALLOWED_STAGES:
|
||||
abort_with_message('Invalid stage: %s' % stage)
|
||||
return stage
|
||||
|
||||
def validate_branch(stage):
|
||||
branch = git_get_current_branch()
|
||||
if not branch:
|
||||
abort_with_message('The current folder is not a git repository.')
|
||||
if branch == 'master' and stage != 'snapshot':
|
||||
abort_with_message('You need to swtich to a release branch for a beta or a final release.')
|
||||
return branch
|
||||
|
||||
def validate_version(stage, branch, version):
|
||||
if stage == 'snapshot' and branch == 'master' and version != CURRENT_VERSION_TAG:
|
||||
abort_with_message('You cannot specify a custom version if you are building a snapshot from master.')
|
||||
|
||||
if not version or version == CURRENT_VERSION_TAG:
|
||||
version = get_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN)
|
||||
|
||||
if stage == 'snapshot':
|
||||
if not 'SNAPSHOT' in version:
|
||||
abort_with_message('Version should contain the word SNAPSHOT: %s' % version)
|
||||
elif stage == 'beta':
|
||||
if not ALLOWED_PRE_RELEASE[0] in version:
|
||||
abort_with_message('Version should contain the word %s: %s' % (ALLOWED_PRE_RELEASE[0], version))
|
||||
elif stage == 'final':
|
||||
if not version or 'SNAPSHOT' in version or ALLOWED_PRE_RELEASE[0] in version:
|
||||
abort_with_message('Version cannot be empty, or contain the words SNAPSHOT or %s: %s' % (ALLOWED_PRE_RELEASE[0], version))
|
||||
|
||||
return version
|
||||
|
||||
def publish_snapshot(branch, version):
|
||||
click.echo('Publishing snapshot for branch: %s (version: %s).' % (branch, version))
|
||||
if branch != 'master':
|
||||
dirty_gradle = update_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN, version=version)
|
||||
if dirty_gradle:
|
||||
git_add(path=GRADLE_PROPERTIES_PATH)
|
||||
git_commit_and_push(branch=branch, version=version)
|
||||
do_circleci_request(branch=branch)
|
||||
|
||||
def publish_beta(branch, version):
|
||||
click.echo('Publishing beta from branch: %s (version: %s).' % (branch, version))
|
||||
dirty_gradle = update_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN, version=version)
|
||||
if dirty_gradle:
|
||||
git_add(path=GRADLE_PROPERTIES_PATH)
|
||||
git_commit_and_push(branch=branch, version=version)
|
||||
do_circleci_request(branch=branch)
|
||||
|
||||
def publish_final(branch, version):
|
||||
click.echo('Publishing final release from branch: %s (version: %s).' % (branch, version))
|
||||
dirty_gradle = update_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN, version=version)
|
||||
if dirty_gradle:
|
||||
git_add(path=GRADLE_PROPERTIES_PATH)
|
||||
if dirty_gradle:
|
||||
git_commit_and_push(branch=branch, version=version)
|
||||
do_circleci_request(branch=branch)
|
||||
|
||||
#
|
||||
# Utils
|
||||
#
|
||||
|
||||
def abort_with_message(message):
|
||||
click.echo(message)
|
||||
click.get_current_context().abort()
|
||||
|
||||
def execute_call(command):
|
||||
click.echo('Executing: %s' % command)
|
||||
result = subprocess.call(command, shell=True)
|
||||
if result != 0:
|
||||
abort_with_message('Command failed: %s' % command)
|
||||
|
||||
#
|
||||
# CircleCI
|
||||
#
|
||||
|
||||
def get_circleci_api_token():
|
||||
circleci_api_token = os.environ.get(CIRCLECI_API_TOKEN_ENV_VAR)
|
||||
if not circleci_api_token:
|
||||
abort_with_message('You need to set the CIRCLECI_API_TOKEN environment variable.')
|
||||
click.echo('Found CircleCI API token.')
|
||||
return circleci_api_token
|
||||
|
||||
def do_circleci_request(branch):
|
||||
url = URL_CIRCLECI + branch
|
||||
params = {'circle-token': get_circleci_api_token()}
|
||||
click.echo('CircleCI request to %s (params: %s)' % (url, json.dumps(params)))
|
||||
click.confirm('\nDo you want to start a build?', abort=True)
|
||||
|
||||
r = requests.post(url, params=params)
|
||||
click.echo('- CircleCI response code: %s' % r.status_code)
|
||||
|
||||
#
|
||||
# Git
|
||||
#
|
||||
|
||||
def git_get_current_branch():
|
||||
return subprocess.check_output('git symbolic-ref --short HEAD'.split(' ')).strip()
|
||||
|
||||
def git_add(path):
|
||||
execute_call(command='git add %s' % path)
|
||||
|
||||
def git_commit_and_push(branch, version):
|
||||
message = '[android] [auto] Update properties to version %s in preparation for build.' % version
|
||||
commands = [
|
||||
'git commit -m "%s"' % message,
|
||||
'git push -u origin %s' % branch]
|
||||
for command in commands:
|
||||
execute_call(command=command)
|
||||
|
||||
#
|
||||
# Read and update properties files
|
||||
#
|
||||
|
||||
def get_current_version(file_path, file_var):
|
||||
click.echo('Getting current version from %s.' % file_path)
|
||||
with open(file_path, 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith(file_var):
|
||||
version_name = line[len(file_var):].strip()
|
||||
click.echo('Current version is %s.' % version_name)
|
||||
return version_name
|
||||
return None
|
||||
|
||||
def update_current_version(file_path, file_var, version):
|
||||
dirty = False
|
||||
click.echo('Updating file to version %s: %s.' % (version, file_path))
|
||||
with open(file_path, 'r') as f:
|
||||
file_lines = f.readlines()
|
||||
for line_number in range(len(file_lines)):
|
||||
if file_lines[line_number].startswith(file_var):
|
||||
content_old = file_lines[line_number]
|
||||
content_new = '%s%s\n' % (file_var, version)
|
||||
if (content_old != content_new):
|
||||
click.echo('%s -> %s' % (content_old.strip(), content_new.strip()))
|
||||
file_lines[line_number] = content_new
|
||||
dirty = True
|
||||
if dirty:
|
||||
with open(file_path, 'w') as f:
|
||||
f.writelines(file_lines)
|
||||
else:
|
||||
click.echo('File already has the right version.')
|
||||
return dirty
|
||||
|
||||
if __name__ == '__main__':
|
||||
release()
|
||||
43
scripts/run-render-test.py
Normal file
43
scripts/run-render-test.py
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
catPath = os.getcwd() + "/platform/android/build/render-test/"
|
||||
failCounter = 0
|
||||
testCounter = 0
|
||||
for cat in os.listdir(catPath):
|
||||
testPath = catPath + cat + "/"
|
||||
for test in os.listdir(testPath):
|
||||
inputPath = os.getcwd() + "/mapbox-gl-js/test/integration/render-tests/" + cat + "/" + test
|
||||
outputPath = testPath + test
|
||||
|
||||
expected = outputPath + "/expected.png"
|
||||
actual = outputPath + "/actual.png"
|
||||
output = outputPath + "/output.png"
|
||||
|
||||
shutil.copyfile(inputPath + "/expected.png", expected)
|
||||
shutil.copyfile(inputPath + "/style.json", outputPath + "/style.json")
|
||||
pixelmatchCommand = "node_modules/pixelmatch/bin/pixelmatch " + actual + " " + expected + " " + output + " 0.1"
|
||||
|
||||
print "Pixel match " + cat + " " + test
|
||||
|
||||
with os.popen(pixelmatchCommand) as f:
|
||||
for line_terminated in f:
|
||||
line = line_terminated.rstrip('\n')
|
||||
pair = line.split(": ")
|
||||
if pair[0] == "error":
|
||||
testCounter += 1
|
||||
currentError = pair[1]
|
||||
if currentError != "0%":
|
||||
failCounter += 1
|
||||
# below deletes successful results
|
||||
# print cat + " " + test + " " + currentError
|
||||
# else:
|
||||
# try:
|
||||
# if os.path.isdir(outputPath):
|
||||
# shutil.rmtree(outputPath)
|
||||
# except OSError, e:
|
||||
# print ("Error: %s - %s." % (e.filename, e.strerror))
|
||||
print
|
||||
print str(failCounter) + "/" + str(testCounter)
|
||||
17
scripts/validate-license.py
Normal file
17
scripts/validate-license.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from subprocess import call
|
||||
from subprocess import Popen, PIPE
|
||||
import sys
|
||||
|
||||
## Run license generation
|
||||
call('cd ../../ && make android-license', shell=True)
|
||||
|
||||
## Git diff changes
|
||||
p = Popen(['git', 'diff', '--name-only', 'LICENSE.md'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
output, err = p.communicate(b"input data that is passed to subprocess' stdin")
|
||||
if b"platform/android/LICENSE.md" in output:
|
||||
raise ValueError("""An error ocurred while validating the license generation.
|
||||
Changes were detected to the license generation output
|
||||
but weren't commited. Run make android-license and
|
||||
commit the changeset to make this validation pass.""")
|
||||
Reference in New Issue
Block a user