Merge branch 'master' of github.com:mapbox/llmr-native

This commit is contained in:
Justin R. Miller
2014-05-29 14:42:36 -07:00
84 changed files with 2156 additions and 788 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.DS_Store
*.o
*.xcodeproj
*.o

View File

@@ -4,6 +4,11 @@ compiler:
- gcc
- clang
env:
matrix:
- BUILDTYPE=Release
- BUILDTYPE=Debug
cache:
- apt
- directories:
@@ -18,14 +23,21 @@ before_install:
install:
- ./setup-libraries.sh
- rm -rf mapnik-packaging/out/packages
before_script:
- if [ ${CXX} = "g++" ]; then export CXX="g++-4.8" ; fi
- if [[ ${CXX} == "g++" ]]; then export CXX="g++-4.8" ; fi
- if [[ $BUILDTYPE} == "Debug" ]]; then export CXXFLAGS="-fsanitize=address -fsanitize=thread";export LDFLAGS="-fsanitize=address -fsanitize=thread"
script:
- make llmr
- make linux
- make run-tests
- make llmr BUILDTYPE=${BUILDTYPE}
- make clean
- make linux BUILDTYPE=${BUILDTYPE}
- make clean
- make run-tests BUILDTYPE=${BUILDTYPE}
# TODO - port to linux
#- make run-headless-test BUILDTYPE=${BUILDTYPE}
notifications:
- hipchat: WB52YkcbCGMbNcStsGeaoRO7cyBSK4wX2ZHo2Y4b@GL

View File

@@ -9,7 +9,7 @@ all: llmr
# Builds the regular library
llmr: config.gypi llmr.gyp node
deps/run_gyp llmr.gyp --depth=. -Goutput_dir=.. --generator-output=./build/llmr -f make
make -C build/llmr V=$(V) llmr-x86
$(MAKE) -C build/llmr BUILDTYPE=$(BUILDTYPE) V=$(V) llmr-x86
node:
@if [ ! `which node` ]; then echo 'error: depends on node.js. please make sure node is on your PATH'; exit 1; fi;
@@ -20,7 +20,7 @@ build/test/Makefile: src common config.gypi test/test.gyp
deps/run_gyp test/test.gyp --depth=. -Goutput_dir=.. --generator-output=./build/test -f make
test: build/test/Makefile
make -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) test
$(MAKE) -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) test
# Runs all test cases
run-tests: test
@@ -29,13 +29,13 @@ run-tests: test
done
test/%:
make -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) $*
$(MAKE) -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) $*
build/$(BUILDTYPE)/test_$*
# Only runs headless test case
run-headless-test: build/test/Makefile
make -C build/test BUILDTYPE=Debug V=$(V) headless
build/Debug/test_headless
$(MAKE) -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) headless
build/$(BUILDTYPE)/test_headless
##### Makefile builds ##########################################################
@@ -44,7 +44,7 @@ run-headless-test: build/test/Makefile
# Builds the linux app with make. This is also used by Travis CI
linux: config.gypi linux/llmr-app.gyp node
deps/run_gyp linux/llmr-app.gyp --depth=. -Goutput_dir=.. --generator-output=./build/linux -f make
make -C build/linux V=$(V) linuxapp
$(MAKE) -C build/linux BUILDTYPE=$(BUILDTYPE) V=$(V) linuxapp
# Executes the Linux binary
run-linux: linux
@@ -55,7 +55,7 @@ run-linux: linux
# Builds the OS X app with make.
osx: config.gypi macosx/llmr-app.gyp node
deps/run_gyp macosx/llmr-app.gyp --depth=. -Goutput_dir=.. --generator-output=./build/macosx -f make
make -C build/macosx V=$(V) osxapp
$(MAKE) -C build/macosx BUILDTYPE=$(BUILDTYPE) V=$(V) osxapp
# Executes the OS X binary
run-osx: osx
@@ -93,11 +93,11 @@ lproj: config.gypi linux/llmr-app.gyp clear_xcode_cache node
##### Maintenace operations ####################################################
clean: clear_xcode_cache
-rm -rf ./build/Release
-rm -rf ./build/Debug
-rm -f include/llmr/shader/shaders.hpp
-rm -f include/llmr/style/resources.hpp
-rm -f src/style/resources.cpp
-find ./deps/gyp -name "*.pyc" -exec rm {} \;
-rm -rf ./build/
-rm -f ./include/llmr/shader/shaders.hpp
-rm -f ./include/llmr/style/resources.hpp
-rm -f ./src/style/resources.cpp
distclean: clean
-rm -rf ./build

View File

@@ -267,6 +267,7 @@ module.exports = {
"value": 10,
"path": "curve",
"text_field": "ele",
"font": "Open Sans Regular, Arial Unicode MS Regular",
"fontSize": 10,
"feature_type": "line",
"type": "text"
@@ -648,6 +649,7 @@ module.exports = {
"layer": "country_label",
"text_field": "name",
"path": "horizontal",
"font": "Open Sans Bold, Arial Unicode MS Bold",
"fontSize": 24,
"feature_type": "point",
"type": "text"
@@ -664,6 +666,7 @@ module.exports = {
"type": "text",
"text_field": "name",
"path": "curve",
"font": "Open Sans Italic, Arial Unicode MS Regular",
"fontSize": 16
},
"marine_label_point_1": {
@@ -675,6 +678,7 @@ module.exports = {
"path": "horizontal",
"field": "labelrank",
"value": 1,
"font": "Open Sans Italic, Arial Unicode MS Regular",
"fontSize": 30
},
"marine_label_point_2": {
@@ -686,6 +690,7 @@ module.exports = {
"path": "horizontal",
"field": "labelrank",
"value": 2,
"font": "Open Sans Italic, Arial Unicode MS Regular",
"fontSize": 24
},
"marine_label_point_other": {
@@ -697,6 +702,7 @@ module.exports = {
"path": "horizontal",
"field": "labelrank",
"value": [3,4,5,6],
"font": "Open Sans Italic, Arial Unicode MS Regular",
"fontSize": 18
},
"state_label": {
@@ -704,19 +710,12 @@ module.exports = {
"layer": "state_label",
"text_field": "name",
"path": "horizontal",
"font": "Open Sans Regular, Arial Unicode MS Regular",
"fontSize": 16,
"feature_type": "point",
"type": "text",
"enabled": 4
},
"place_label_city_point": {
"source": "outdoors",
"layer": "place_label",
"field": "type",
"value": "city",
"type": "point",
"size": 4
},
"place_label_city": {
"source": "outdoors",
"layer": "place_label",
@@ -724,6 +723,7 @@ module.exports = {
"value": "city",
"text_field": "name",
"path": "horizontal",
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 20,
"feature_type": "point",
"type": "text"
@@ -735,6 +735,7 @@ module.exports = {
"value": "town",
"text_field": "name",
"path": "horizontal",
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 24,
"feature_type": "point",
"type": "text"
@@ -746,6 +747,7 @@ module.exports = {
"value": "village",
"text_field": "name",
"path": "horizontal",
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 22,
"feature_type": "point",
"type": "text"
@@ -761,17 +763,49 @@ module.exports = {
],
"text_field": "name",
"path": "horizontal",
"fontSize": 14,
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 18,
"feature_type": "point",
"type": "text"
},
"road_label": {
"road_label_1": {
"source": "outdoors",
"layer": "road_label",
"field": "class",
"value": ["motorway","main"],
"text_field": "name",
"path": "curve",
"padding": 2,
"fontSize": 13,
"font": "Open Sans Regular, Arial Unicode MS Regular",
"fontSize": 18,
"feature_type": "line",
"type": "text",
"maxAngleDelta": 0.5
},
"road_label_2": {
"source": "outdoors",
"layer": "road_label",
"field": "class",
"value": ["street","street_limited"],
"text_field": "name",
"path": "curve",
"padding": 2,
"font": "Open Sans Regular, Arial Unicode MS Regular",
"fontSize": 16,
"feature_type": "line",
"type": "text",
"maxAngleDelta": 0.5
},
"road_label_3": {
"source": "outdoors",
"layer": "road_label",
"field": "class",
"value": ["service","driveway","path"],
"text_field": "name",
"path": "curve",
"padding": 2,
"font": "Open Sans Regular, Arial Unicode MS Regular",
"fontSize": 14,
"feature_type": "line",
"type": "text",
"maxAngleDelta": 0.5
@@ -781,6 +815,7 @@ module.exports = {
"layer": "water_label",
"text_field": "name",
"path": "horizontal",
"font": "Open Sans Semibold Italic, Arial Unicode MS Bold",
"fontSize": 12,
"feature_type": "point",
"type": "text"
@@ -790,6 +825,7 @@ module.exports = {
"layer": "waterway_label",
"text_field": "name",
"path": "curve",
"font": "Open Sans Semibold Italic, Arial Unicode MS Bold",
"fontSize": 12,
"textMinDistance": 15,
"feature_type": "line",
@@ -800,7 +836,25 @@ module.exports = {
"layer": "poi_label",
"icon": "maki",
"field": "scalerank",
"value": [1, 2, 3],
"value": [1, 2],
"size": 12,
"type": "point"
},
"poi_3": {
"source": "outdoors",
"layer": "poi_label",
"icon": "maki",
"field": "scalerank",
"value": 3,
"size": 12,
"type": "point"
},
"poi_4": {
"source": "outdoors",
"layer": "poi_label",
"icon": "maki",
"field": "scalerank",
"value": 4,
"size": 12,
"type": "point"
},
@@ -813,7 +867,7 @@ module.exports = {
"size": 24,
"type": "point"
},
"poi_label": {
"poi_label_1-2": {
"source": "outdoors",
"layer": "poi_label",
"field": "scalerank",
@@ -824,10 +878,36 @@ module.exports = {
"text_field": "name",
"path": "horizontal",
"padding": 2,
"fontSize": 12,
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 11,
"feature_type": "point",
"type": "text",
"alwaysVisible": true
"type": "text"
},
"poi_label_3": {
"source": "outdoors",
"layer": "poi_label",
"field": "scalerank",
"value": 3,
"text_field": "name",
"path": "horizontal",
"padding": 2,
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 10,
"feature_type": "point",
"type": "text"
},
"poi_label_4": {
"source": "outdoors",
"layer": "poi_label",
"field": "scalerank",
"value": 4,
"text_field": "name",
"path": "horizontal",
"padding": 2,
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 10,
"feature_type": "point",
"type": "text"
}
},
"structure": [
@@ -923,6 +1003,18 @@ module.exports = {
"name": "waterway_stream",
"bucket": "waterway_stream"
},
{
"name": "building_shadow",
"bucket": "building"
},
{
"name": "building",
"bucket": "building"
},
{
"name": "building_wall",
"bucket": "building"
},
{
"name": "hillshade_full_highlight",
"bucket": "hillshade_full_highlight"
@@ -991,18 +1083,6 @@ module.exports = {
"name": "aeroway_taxiway",
"bucket": "aeroway_taxiway"
},
{
"name": "building_shadow",
"bucket": "building"
},
{
"name": "building",
"bucket": "building"
},
{
"name": "building_wall",
"bucket": "building"
},
{
"name": "tunnel_motorway_link_casing",
"bucket": "tunnel_motorway_link"
@@ -1279,10 +1359,6 @@ module.exports = {
"name": "state_label",
"bucket": "state_label"
},
{
"name": "place_label_city_point",
"bucket": "place_label_city_point"
},
{
"name": "place_label_city",
"bucket": "place_label_city"
@@ -1300,12 +1376,16 @@ module.exports = {
"bucket": "place_label_other"
},
{
"name": "poi_label",
"bucket": "poi_label"
"name": "road_label_1",
"bucket": "road_label_1"
},
{
"name": "road_label",
"bucket": "road_label"
"name": "road_label_2",
"bucket": "road_label_2"
},
{
"name": "road_label_3",
"bucket": "road_label_3"
},
{
"name": "contour_label",
@@ -1323,6 +1403,26 @@ module.exports = {
"name": "poi",
"bucket": "poi"
},
{
"name": "poi_label_1-2",
"bucket": "poi_label_1-2"
},
{
"name": "poi_3",
"bucket": "poi_3"
},
{
"name": "poi_label_3",
"bucket": "poi_label_3"
},
{
"name": "poi_4",
"bucket": "poi_4"
},
{
"name": "poi_label_4",
"bucket": "poi_label_4"
},
{
"name": "poi_aerodrome",
"bucket": "poi_aerodrome"
@@ -1556,12 +1656,27 @@ module.exports = {
{"z": 8, "val": 1},
{"z": 12, "val": 2}
],
"road_label_size": [
"road_label_1_size": [
"stops",
{"z": 0, "val": 12},
{"z": 13, "val": 11},
{"z": 14, "val": 12},
{"z": 15, "val": 13},
{"z": 22, "val": 13}
{"z": 16, "val": 14},
{"z": 17, "val": 16},
{"z": 18, "val": 18}
],
"road_label_2_size": [
"stops",
{"z": 13, "val": 11},
{"z": 14, "val": 12},
{"z": 16, "val": 14},
{"z": 18, "val": 16}
],
"road_label_3_size": [
"stops",
{"z": 15, "val": 10},
{"z": 16, "val": 12},
{"z": 18, "val": 14}
],
"fence_width": [
"stops",
@@ -1584,6 +1699,11 @@ module.exports = {
{"z": 19, "val": 12},
{"z": 20, "val": 24},
{"z": 21, "val": 48}
],
"country_label_size": [
"stops",
{"z": 1, "val": 14},
{"z": 12, "val": 24}
]
},
"classes": [
@@ -1738,6 +1858,7 @@ module.exports = {
"color": "#103",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
@@ -1750,6 +1871,7 @@ module.exports = {
"color": "#206",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
@@ -1762,6 +1884,7 @@ module.exports = {
"color": "#fffff3",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
@@ -1774,6 +1897,7 @@ module.exports = {
"color": "#ffd",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
@@ -2127,11 +2251,8 @@ module.exports = {
"country_label": {
"color": "country_text",
"stroke": "rgba(255,255,255,0.5)",
"size": [
"stops",
{"z": 6, "val": 14},
{"z": 12, "val": 24}
]
"strokeWidth": 0.5,
"size": "country_label_size"
},
"country_label_line": {
"color": "country_text",
@@ -2145,7 +2266,6 @@ module.exports = {
"marine_label_point_1": {
"color": "#a9c4c7",
"size": ["stops",
{"z": 0, "val": 20},
{"z": 3, "val": 20},
{"z": 4, "val": 25},
{"z": 5, "val": 30},
@@ -2156,7 +2276,6 @@ module.exports = {
"marine_label_point_2": {
"color": "#a9c4c7",
"size": ["stops",
{"z": 0, "val": 13},
{"z": 3, "val": 13},
{"z": 4, "val": 14},
{"z": 5, "val": 20},
@@ -2168,7 +2287,6 @@ module.exports = {
"marine_label_point_other": {
"color": "#a9c4c7",
"size": ["stops",
{"z": 0, "val": 12},
{"z": 3, "val": 12},
{"z": 4, "val": 13},
{"z": 5, "val": 15},
@@ -2190,9 +2308,6 @@ module.exports = {
{"z": 10, "val": 0}
]
},
"place_label_city_point": {
"color": "#4a4032"
},
"place_label_city": {
"color": "#444",
"strokeWidth": 0.4,
@@ -2204,20 +2319,12 @@ module.exports = {
{"z": 7, "val": 14},
{"z": 14.99, "val": 20},
{"z": 15, "val": 0}
],
"translate": [
0,
[
"stops",
{"z": 4, "val": 10},
{"z": 6, "val": 30},
{"z": 7, "val": 0}
]
]
},
"place_label_town": {
"color": "#716656",
"strokeWidth": 0.5,
"strokeWidth": 0.3,
"strokeBlur": 2,
"stroke": "text_stroke",
"size": [
"stops",
@@ -2229,7 +2336,8 @@ module.exports = {
},
"place_label_village": {
"color": "#635644",
"strokeWidth": 0.5,
"strokeWidth": 0.3,
"strokeBlur": 2,
"stroke": "text_stroke",
"size": [
"stops",
@@ -2245,18 +2353,32 @@ module.exports = {
"stroke": "text_stroke",
"size": [
"stops",
{"z": 0, "val": 10},
{"z": 14, "val": 11},
{"z": 15, "val": 12},
{"z": 16, "val": 14}
{"z": 13, "val": 11},
{"z": 14, "val": 12},
{"z": 16, "val": 14},
{"z": 18, "val": 18}
]
},
"road_label": {
"road_label_1": {
"color": "#585042",
"stroke": "land",
"strokeWidth": 0.6,
"strokeBlur": 2,
"size": "road_label_size"
"size": "road_label_1_size"
},
"road_label_2": {
"color": "#585042",
"stroke": "land",
"strokeWidth": 0.6,
"strokeBlur": 2,
"size": "road_label_2_size"
},
"road_label_3": {
"color": "#585042",
"stroke": "land",
"strokeWidth": 0.6,
"strokeBlur": 2,
"size": "road_label_3_size"
},
"water_label": {
"color": "water_dark",
@@ -2269,18 +2391,56 @@ module.exports = {
"stroke": "text_stroke"
},
"poi": {
//"color": "rgba(50,50,50,0.9)",
"antialias": false
//"stroke": #000,
//"strokeWidth": 0.4,
//"strokeBlur": 2
},
"poi_label": {
"poi_3": {
"antialias": false,
"opacity": [
"stops",
{"z": 16.5, "val": 0},
{"z": 16.75, "val": 1}
]
},
"poi_4": {
"antialias": false,
"opacity": [
"stops",
{"z": 18.5, "val": 0},
{"z": 18.75, "val": 1}
]
},
"poi_label_1-2": {
"color": "#444",
"size": 11,
"stroke": "land",
"strokeWidth": 0.3,
"strokeBlur": 1,
"translate": [0, 14]
},
"poi_label_3": {
"color": "#444",
"size": 10,
"stroke": "land",
"strokeWidth": 0.6,
"strokeBlur": 2,
"strokeWidth": 0.3,
"strokeBlur": 1,
"translate": [0, 14],
"opacity": [
"stops",
{"z": 16.5, "val": 0},
{"z": 16.75, "val": 1}
]
},
"poi_label_4": {
"color": "#444",
"size": 10,
"opacity": [
"stops",
{"z": 18.5, "val": 0},
{"z": 18.75, "val": 1}
],
"stroke": "land",
"strokeWidth": 0.3,
"strokeBlur": 1,
"translate": [0, 14]
},
"poi_aerodrome": {
@@ -2299,7 +2459,7 @@ module.exports = {
"color": "land_night"
},
"admin_maritime_cover": {
"color": "#0a1347",
"color": "water_night",
"width": 5
},
"admin_maritime": {
@@ -2450,10 +2610,11 @@ module.exports = {
"color": "#103",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
{"z": 0, "val": 0.3},
{"z": 16, "val": 0.3},
{"z": 17, "val": 0.2},
{"z": 18, "val": 0.1}
]
@@ -2462,10 +2623,11 @@ module.exports = {
"color": "#206",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
{"z": 0, "val": 0.3},
{"z": 16, "val": 0.3},
{"z": 17, "val": 0.2},
{"z": 18, "val": 0.1}
]
@@ -2474,6 +2636,7 @@ module.exports = {
"color": "#fdfdad",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
@@ -2487,6 +2650,7 @@ module.exports = {
"color": "#ffe1b7",
"antialias": false,
"prerender": true,
"prerender-size": 1024,
"prerender-blur": 1,
"opacity": [
"stops",
@@ -2835,12 +2999,9 @@ module.exports = {
},
"country_label": {
"color": "text_night",
"stroke": "text_stroke_night",
"size": [
"stops",
{"z": 6, "val": 14},
{"z": 12, "val": 24}
]
"stroke": "text2_stroke_night",
"strokeWidth": 0.5,
"size": "country_label_size"
},
"country_label_line": {
"color": "text_night",
@@ -2854,7 +3015,6 @@ module.exports = {
"marine_label_point_1": {
"color": "water_dark_night",
"size": ["stops",
{"z": 0, "val": 20},
{"z": 3, "val": 20},
{"z": 4, "val": 25},
{"z": 5, "val": 30},
@@ -2865,7 +3025,6 @@ module.exports = {
"marine_label_point_2": {
"color": "water_dark_night",
"size": ["stops",
{"z": 0, "val": 13},
{"z": 3, "val": 13},
{"z": 4, "val": 14},
{"z": 5, "val": 20},
@@ -2877,7 +3036,6 @@ module.exports = {
"marine_label_point_other": {
"color": "#a9c4c7",
"size": ["stops",
{"z": 0, "val": 12},
{"z": 3, "val": 12},
{"z": 4, "val": 13},
{"z": 5, "val": 15},
@@ -2899,13 +3057,10 @@ module.exports = {
{"z": 10, "val": 0}
]
},
"place_label_city_point": {
"color": "#ddd"
},
"place_label_city": {
"color": "#fff",
"strokeWidth": 0.4,
"stroke": "text_stroke_night",
"stroke": "text2_stroke_night",
"size": [
"stops",
{"z": 3.99, "val": 0},
@@ -2913,21 +3068,13 @@ module.exports = {
{"z": 7, "val": 14},
{"z": 14.99, "val": 20},
{"z": 15, "val": 0}
],
"translate": [
0,
[
"stops",
{"z": 4, "val": 10},
{"z": 6, "val": 30},
{"z": 7, "val": 0}
]
]
},
"place_label_town": {
"color": "text_night",
"strokeWidth": 0.5,
"stroke": "text_stroke_night",
"strokeWidth": 0.3,
"strokeBlur": 2,
"stroke": "text2_stroke_night",
"size": [
"stops",
{"z": 9, "val": 10},
@@ -2938,8 +3085,9 @@ module.exports = {
},
"place_label_village": {
"color": "text_night",
"strokeWidth": 0.5,
"stroke": "text_stroke_night",
"strokeWidth": 0.3,
"strokeBlur": 2,
"stroke": "text2_stroke_night",
"size": [
"stops",
{"z": 9, "val": 8},
@@ -2951,43 +3099,100 @@ module.exports = {
},
"place_label_other": {
"color": "text_night",
"stroke": "text_stroke_night",
"strokeWidth": 0.5,
"strokeBlur": 3,
"stroke": "text2_stroke_night",
"strokeWidth": 0.3,
"strokeBlur": 2,
"size": [
"stops",
{"z": 0, "val": 10},
{"z": 14, "val": 11},
{"z": 15, "val": 12},
{"z": 16, "val": 14}
{"z": 13, "val": 11},
{"z": 14, "val": 12},
{"z": 16, "val": 14},
{"z": 18, "val": 18}
]
},
"road_label": {
"road_label_1": {
"color": "text_night",
"stroke": "text2_stroke_night",
"strokeWidth": 0.5,
"strokeBlur": 3,
"size": "road_label_size"
"size": "road_label_1_size"
},
"road_label_2": {
"color": "text_night",
"stroke": "text2_stroke_night",
"strokeWidth": 0.5,
"strokeBlur": 3,
"size": "road_label_2_size"
},
"road_label_3": {
"color": "text_night",
"stroke": "text2_stroke_night",
"strokeWidth": 0.5,
"strokeBlur": 3,
"size": "road_label_3_size"
},
"water_label": {
"color": "water_dark_night",
"stroke": "text_stroke_night"
"strokeWidth": 0.8,
"stroke": "text2_stroke_night"
},
"waterway_label": {
"color": "land_night",
"color": "water_dark_night",
"strokeWidth": 0.8,
"stroke": "water_night"
"stroke": "text2_stroke_night"
},
"poi": {
"color": "white",
"antialias": false
},
"poi_label": {
"poi_3": {
"antialias": false,
"opacity": [
"stops",
{"z": 16.5, "val": 0},
{"z": 16.75, "val": 1}
]
},
"poi_4": {
"antialias": false,
"opacity": [
"stops",
{"z": 18.5, "val": 0},
{"z": 18.75, "val": 1}
]
},
"poi_label_1-2": {
"color": "#fff",
"size": 11,
"stroke": "text2_stroke_night",
"strokeWidth": 0.3,
"strokeBlur": 1,
"translate": [0, 14]
},
"poi_label_3": {
"color": "#fff",
"size": 10,
"stroke": "text2_stroke_night",
"strokeWidth": 0.3,
"strokeBlur": 2,
"strokeBlur": 1,
"translate": [0, 14],
"opacity": [
"stops",
{"z": 16.5, "val": 0},
{"z": 16.75, "val": 1}
]
},
"poi_label_4": {
"color": "#fff",
"size": 10,
"opacity": [
"stops",
{"z": 18.5, "val": 0},
{"z": 18.75, "val": 1}
],
"stroke": "text2_stroke_night",
"strokeWidth": 0.3,
"strokeBlur": 1,
"translate": [0, 14]
},
"poi_aerodrome": {

View File

@@ -5,6 +5,11 @@
'CLANG_CXX_LIBRARY': 'libc++',
'CLANG_CXX_LANGUAGE_STANDARD':'c++11',
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'GCC_ENABLE_CPP_RTTI':'YES',
'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra' ],
'GCC_WARN_PEDANTIC': 'YES',
'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE'
},
'cflags_cc': ['-std=c++11'],
'configurations': {
@@ -14,7 +19,11 @@
'xcode_settings': {
'GCC_OPTIMIZATION_LEVEL': '0',
'GCC_GENERATE_DEBUGGING_SYMBOLS': 'YES',
'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra', '-pedantic' ]
'DEAD_CODE_STRIPPING': 'NO',
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'NO',
'RUN_CLANG_STATIC_ANALYZER':'YES',
'CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY': 'YES',
'CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER': 'YES'
}
},
'Release': {
@@ -24,8 +33,7 @@
'GCC_OPTIMIZATION_LEVEL': '3',
'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO',
'DEAD_CODE_STRIPPING': 'YES',
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra', '-pedantic' ]
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES'
}
}
}

View File

@@ -16,8 +16,6 @@ PYLINT_BLACKLIST = [
'test/lib/TestCmd.py',
'test/lib/TestCommon.py',
'test/lib/TestGyp.py',
# Needs style fix.
'pylib/gyp/generator/xcode.py',
]

10
deps/gyp/codereview.settings vendored Normal file
View File

@@ -0,0 +1,10 @@
# This file is used by gcl to get repository specific information.
CODE_REVIEW_SERVER: codereview.chromium.org
CC_LIST: gyp-developer@googlegroups.com
VIEW_VC: http://code.google.com/p/gyp/source/detail?r=
TRY_ON_UPLOAD: True
TRYSERVER_PROJECT: gyp
TRYSERVER_PATCHLEVEL: 0
TRYSERVER_ROOT: trunk
TRYSERVER_SVN_URL: svn://svn.chromium.org/chrome-try/try-nacl

View File

@@ -367,6 +367,35 @@ fix_vc_macro_slashes_regex = re.compile(
r'(\$\((?:%s)\))(?:[\\/]+)' % "|".join(fix_vc_macro_slashes_regex_list)
)
# Regular expression to detect keys that were generated by exclusion lists
_EXCLUDED_SUFFIX_RE = re.compile('^(.*)_excluded$')
def _ValidateExclusionSetting(setting, settings, error_msg, stderr=sys.stderr):
"""Verify that 'setting' is valid if it is generated from an exclusion list.
If the setting appears to be generated from an exclusion list, the root name
is checked.
Args:
setting: A string that is the setting name to validate
settings: A dictionary where the keys are valid settings
error_msg: The message to emit in the event of error
stderr: The stream receiving the error messages.
"""
# This may be unrecognized because it's an exclusion list. If the
# setting name has the _excluded suffix, then check the root name.
unrecognized = True
m = re.match(_EXCLUDED_SUFFIX_RE, setting)
if m:
root_setting = m.group(1)
unrecognized = root_setting not in settings
if unrecognized:
# We don't know this setting. Give a warning.
print >> stderr, error_msg
def FixVCMacroSlashes(s):
"""Replace macros which have excessive following slashes.
@@ -403,9 +432,6 @@ def ConvertVCMacrosToMSBuild(s):
return s
_EXCLUDED_SUFFIX_RE = re.compile('^(.*)_excluded$')
def ConvertToMSBuildSettings(msvs_settings, stderr=sys.stderr):
"""Converts MSVS settings (VS2008 and earlier) to MSBuild settings (VS2010+).
@@ -432,19 +458,12 @@ def ConvertToMSBuildSettings(msvs_settings, stderr=sys.stderr):
print >> stderr, ('Warning: while converting %s/%s to MSBuild, '
'%s' % (msvs_tool_name, msvs_setting, e))
else:
# This may be unrecognized because it's an exclusion list. If the
# setting name has the _excluded suffix, then check the root name.
unrecognized = True
m = re.match(_EXCLUDED_SUFFIX_RE, msvs_setting)
if m:
root_msvs_setting = m.group(1)
unrecognized = root_msvs_setting not in msvs_tool
if unrecognized:
# We don't know this setting. Give a warning.
print >> stderr, ('Warning: unrecognized setting %s/%s '
'while converting to MSBuild.' %
(msvs_tool_name, msvs_setting))
_ValidateExclusionSetting(msvs_setting,
msvs_tool,
('Warning: unrecognized setting %s/%s '
'while converting to MSBuild.' %
(msvs_tool_name, msvs_setting)),
stderr)
else:
print >> stderr, ('Warning: unrecognized tool %s while converting to '
'MSBuild.' % msvs_tool_name)
@@ -495,8 +514,12 @@ def _ValidateSettings(validators, settings, stderr):
print >> stderr, ('Warning: for %s/%s, %s' %
(tool_name, setting, e))
else:
print >> stderr, ('Warning: unrecognized setting %s/%s' %
(tool_name, setting))
_ValidateExclusionSetting(setting,
tool_validators,
('Warning: unrecognized setting %s/%s' %
(tool_name, setting)),
stderr)
else:
print >> stderr, ('Warning: unrecognized tool %s' % tool_name)
@@ -593,7 +616,9 @@ _Same(_compile, 'DebugInformationFormat',
_Same(_compile, 'EnableEnhancedInstructionSet',
_Enumeration(['NotSet',
'StreamingSIMDExtensions', # /arch:SSE
'StreamingSIMDExtensions2'])) # /arch:SSE2
'StreamingSIMDExtensions2', # /arch:SSE2
'AdvancedVectorExtensions', # /arch:AVX (vs2012+)
'NoExtensions',])) # /arch:IA32 (vs2012+)
_Same(_compile, 'ErrorReporting',
_Enumeration(['None', # /errorReport:none
'Prompt', # /errorReport:prompt
@@ -848,13 +873,6 @@ _Moved(_link, 'UseLibraryDependencyInputs', 'ProjectReference', _boolean)
# MSVS options not found in MSBuild.
_MSVSOnly(_link, 'OptimizeForWindows98', _newly_boolean)
_MSVSOnly(_link, 'UseUnicodeResponseFiles', _boolean)
# These settings generate correctly in the MSVS output files when using
# e.g. DelayLoadDLLs! or AdditionalDependencies! to exclude files from
# configuration entries, but result in spurious artifacts which can be
# safely ignored here. See crbug.com/246570
_MSVSOnly(_link, 'AdditionalLibraryDirectories_excluded', _folder_list)
_MSVSOnly(_link, 'DelayLoadDLLs_excluded', _file_list)
_MSVSOnly(_link, 'AdditionalDependencies_excluded', _file_list)
# MSBuild options not found in MSVS.
_MSBuildOnly(_link, 'BuildingInIDE', _boolean)
@@ -1003,9 +1021,6 @@ _Same(_lib, 'TargetMachine', _target_machine_enumeration)
# ProjectReference. We may want to validate that they are consistent.
_Moved(_lib, 'LinkLibraryDependencies', 'ProjectReference', _boolean)
# TODO(jeanluc) I don't think these are genuine settings but byproducts of Gyp.
_MSVSOnly(_lib, 'AdditionalLibraryDirectories_excluded', _folder_list)
_MSBuildOnly(_lib, 'DisplayLibrary', _string) # /LIST Visible='false'
_MSBuildOnly(_lib, 'ErrorReporting',
_Enumeration([], new=['PromptImmediately', # /ERRORREPORT:PROMPT

View File

@@ -109,15 +109,16 @@ def ShardTargets(target_list, target_dicts):
new_target_dicts[t] = target_dicts[t]
# Shard dependencies.
for t in new_target_dicts:
dependencies = copy.copy(new_target_dicts[t].get('dependencies', []))
new_dependencies = []
for d in dependencies:
if d in targets_to_shard:
for i in range(targets_to_shard[d]):
new_dependencies.append(_ShardName(d, i))
else:
new_dependencies.append(d)
new_target_dicts[t]['dependencies'] = new_dependencies
for deptype in ('dependencies', 'dependencies_original'):
dependencies = copy.copy(new_target_dicts[t].get(deptype, []))
new_dependencies = []
for d in dependencies:
if d in targets_to_shard:
for i in range(targets_to_shard[d]):
new_dependencies.append(_ShardName(d, i))
else:
new_dependencies.append(d)
new_target_dicts[t][deptype] = new_dependencies
return (new_target_list, new_target_dicts)
@@ -264,4 +265,4 @@ def InsertLargePdbShims(target_list, target_dicts, vars):
# Update the original target to depend on the shim target.
target_dict.setdefault('dependencies', []).append(full_shim_target_name)
return (target_list, target_dicts)
return (target_list, target_dicts)

View File

@@ -379,7 +379,7 @@ def SelectVisualStudioVersion(version='auto'):
if version == 'auto':
version = os.environ.get('GYP_MSVS_VERSION', 'auto')
version_map = {
'auto': ('10.0', '12.0', '9.0', '8.0', '11.0'),
'auto': ('12.0', '10.0', '9.0', '8.0', '11.0'),
'2005': ('8.0',),
'2005e': ('8.0',),
'2008': ('9.0',),

View File

@@ -4,6 +4,7 @@
from __future__ import with_statement
import collections
import errno
import filecmp
import os.path
@@ -472,6 +473,72 @@ def uniquer(seq, idfun=None):
return result
# Based on http://code.activestate.com/recipes/576694/.
class OrderedSet(collections.MutableSet):
def __init__(self, iterable=None):
self.end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.map = {} # key --> [key, prev, next]
if iterable is not None:
self |= iterable
def __len__(self):
return len(self.map)
def __contains__(self, key):
return key in self.map
def add(self, key):
if key not in self.map:
end = self.end
curr = end[1]
curr[2] = end[1] = self.map[key] = [key, curr, end]
def discard(self, key):
if key in self.map:
key, prev_item, next_item = self.map.pop(key)
prev_item[2] = next_item
next_item[1] = prev_item
def __iter__(self):
end = self.end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
# The second argument is an addition that causes a pylint warning.
def pop(self, last=True): # pylint: disable=W0221
if not self:
raise KeyError('set is empty')
key = self.end[1][0] if last else self.end[2][0]
self.discard(key)
return key
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, list(self))
def __eq__(self, other):
if isinstance(other, OrderedSet):
return len(self) == len(other) and list(self) == list(other)
return set(self) == set(other)
# Extensions to the recipe.
def update(self, iterable):
for i in iterable:
if i not in self:
self.add(i)
class CycleError(Exception):
"""An exception raised when an unexpected cycle is detected."""
def __init__(self, nodes):

View File

@@ -55,7 +55,7 @@ generator_additional_path_sections = []
generator_extra_sources_for_rules = []
SHARED_FOOTER = """\
ALL_MODULES_FOOTER = """\
# "gyp_all_modules" is a concatenation of the "gyp_all_modules" targets from
# all the included sub-makefiles. This is just here to clarify.
gyp_all_modules:
@@ -133,7 +133,7 @@ class AndroidMkWriter(object):
self.android_top_dir = android_top_dir
def Write(self, qualified_target, relative_target, base_path, output_filename,
spec, configs, part_of_all):
spec, configs, part_of_all, write_alias_target):
"""The main entry point: writes a .mk file for a single target.
Arguments:
@@ -144,6 +144,8 @@ class AndroidMkWriter(object):
output_filename: output .mk file name to write
spec, configs: gyp info
part_of_all: flag indicating this target is part of 'all'
write_alias_target: flag indicating whether to create short aliases for
this target
"""
gyp.common.EnsureDirExists(output_filename)
@@ -186,11 +188,19 @@ class AndroidMkWriter(object):
self.WriteLn('LOCAL_MODULE_TAGS := optional')
if self.toolset == 'host':
self.WriteLn('LOCAL_IS_HOST_MODULE := true')
else:
self.WriteLn('LOCAL_MODULE_TARGET_ARCH := '
'$(TARGET_$(GYP_VAR_PREFIX)ARCH)')
# Grab output directories; needed for Actions and Rules.
self.WriteLn('gyp_intermediate_dir := $(call local-intermediates-dir)')
if self.toolset == 'host':
self.WriteLn('gyp_intermediate_dir := '
'$(call local-intermediates-dir)')
else:
self.WriteLn('gyp_intermediate_dir := '
'$(call local-intermediates-dir,,$(GYP_VAR_PREFIX))')
self.WriteLn('gyp_shared_intermediate_dir := '
'$(call intermediates-dir-for,GYP,shared)')
'$(call intermediates-dir-for,GYP,shared,,,$(GYP_VAR_PREFIX))')
self.WriteLn()
# List files this target depends on so that actions/rules/copies/sources
@@ -226,7 +236,8 @@ class AndroidMkWriter(object):
if spec.get('sources', []) or extra_sources:
self.WriteSources(spec, configs, extra_sources)
self.WriteTarget(spec, configs, deps, link_deps, part_of_all)
self.WriteTarget(spec, configs, deps, link_deps, part_of_all,
write_alias_target)
# Update global list of target outputs, used in dependency tracking.
target_outputs[qualified_target] = ('path', self.output_binary)
@@ -291,6 +302,7 @@ class AndroidMkWriter(object):
# writing duplicate dummy rules for those outputs.
main_output = make.QuoteSpaces(self.LocalPathify(outputs[0]))
self.WriteLn('%s: gyp_local_path := $(LOCAL_PATH)' % main_output)
self.WriteLn('%s: gyp_var_prefix := $(GYP_VAR_PREFIX)' % main_output)
self.WriteLn('%s: gyp_intermediate_dir := '
'$(abspath $(gyp_intermediate_dir))' % main_output)
self.WriteLn('%s: gyp_shared_intermediate_dir := '
@@ -337,13 +349,10 @@ class AndroidMkWriter(object):
"""
if len(rules) == 0:
return
rule_trigger = '%s_rule_trigger' % self.android_module
did_write_rule = False
for rule in rules:
if len(rule.get('rule_sources', [])) == 0:
continue
did_write_rule = True
name = make.StringToMakefileVariable('%s_%s' % (self.relative_target,
rule['rule_name']))
self.WriteLn('\n### Generated for rule "%s":' % name)
@@ -391,6 +400,7 @@ class AndroidMkWriter(object):
outputs = map(self.LocalPathify, outputs)
main_output = outputs[0]
self.WriteLn('%s: gyp_local_path := $(LOCAL_PATH)' % main_output)
self.WriteLn('%s: gyp_var_prefix := $(GYP_VAR_PREFIX)' % main_output)
self.WriteLn('%s: gyp_intermediate_dir := '
'$(abspath $(gyp_intermediate_dir))' % main_output)
self.WriteLn('%s: gyp_shared_intermediate_dir := '
@@ -412,13 +422,9 @@ class AndroidMkWriter(object):
# Make each output depend on the main output, with an empty command
# to force make to notice that the mtime has changed.
self.WriteLn('%s: %s ;' % (output, main_output))
self.WriteLn('.PHONY: %s' % (rule_trigger))
self.WriteLn('%s: %s' % (rule_trigger, main_output))
self.WriteLn('')
if did_write_rule:
extra_sources.append(rule_trigger) # Force all rules to run.
self.WriteLn('### Finished generating for all rules')
self.WriteLn('')
self.WriteLn()
self.WriteLn()
def WriteCopies(self, copies, extra_outputs):
@@ -501,6 +507,9 @@ class AndroidMkWriter(object):
self.WriteLn('LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) '
'$(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))')
self.WriteLn('LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))')
# Android uses separate flags for assembly file invocations, but gyp expects
# the same CFLAGS to be applied:
self.WriteLn('LOCAL_ASFLAGS := $(LOCAL_CFLAGS)')
def WriteSources(self, spec, configs, extra_sources):
@@ -609,16 +618,16 @@ class AndroidMkWriter(object):
prefix = ''
if spec['toolset'] == 'host':
suffix = '_host_gyp'
suffix = '_$(TARGET_$(GYP_VAR_PREFIX)ARCH)_host_gyp'
else:
suffix = '_gyp'
if self.path:
name = '%s%s_%s%s' % (prefix, self.path, self.target, suffix)
middle = make.StringToMakefileVariable('%s_%s' % (self.path, self.target))
else:
name = '%s%s%s' % (prefix, self.target, suffix)
middle = make.StringToMakefileVariable(self.target)
return make.StringToMakefileVariable(name)
return ''.join([prefix, middle, suffix])
def ComputeOutputParts(self, spec):
@@ -680,15 +689,15 @@ class AndroidMkWriter(object):
if self.toolset == 'host':
path = '$(HOST_OUT_INTERMEDIATE_LIBRARIES)'
else:
path = '$(TARGET_OUT_INTERMEDIATE_LIBRARIES)'
path = '$($(GYP_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)'
else:
# Other targets just get built into their intermediate dir.
if self.toolset == 'host':
path = '$(call intermediates-dir-for,%s,%s,true)' % (self.android_class,
self.android_module)
else:
path = '$(call intermediates-dir-for,%s,%s)' % (self.android_class,
self.android_module)
path = ('$(call intermediates-dir-for,%s,%s,,,$(GYP_VAR_PREFIX))'
% (self.android_class, self.android_module))
assert spec.get('product_dir') is None # TODO: not supported?
return os.path.join(path, self.ComputeOutputBasename(spec))
@@ -816,12 +825,15 @@ class AndroidMkWriter(object):
'LOCAL_SHARED_LIBRARIES')
def WriteTarget(self, spec, configs, deps, link_deps, part_of_all):
def WriteTarget(self, spec, configs, deps, link_deps, part_of_all,
write_alias_target):
"""Write Makefile code to produce the final target of the gyp spec.
spec, configs: input from gyp.
deps, link_deps: dependency lists; see ComputeDeps()
part_of_all: flag indicating this target is part of 'all'
write_alias_target: flag indicating whether to create short aliases for this
target
"""
self.WriteLn('### Rules for final target.')
@@ -832,7 +844,7 @@ class AndroidMkWriter(object):
# name 'gyp_all_modules' as the Android build system doesn't allow the use
# of the Make target 'all' and because 'all_modules' is the equivalent of
# the Make target 'all' on Android.
if part_of_all:
if part_of_all and write_alias_target:
self.WriteLn('# Add target alias to "gyp_all_modules" target.')
self.WriteLn('.PHONY: gyp_all_modules')
self.WriteLn('gyp_all_modules: %s' % self.android_module)
@@ -841,7 +853,7 @@ class AndroidMkWriter(object):
# Add an alias from the gyp target name to the Android module name. This
# simplifies manual builds of the target, and is required by the test
# framework.
if self.target != self.android_module:
if self.target != self.android_module and write_alias_target:
self.WriteLn('# Alias gyp target name.')
self.WriteLn('.PHONY: %s' % self.target)
self.WriteLn('%s: %s' % (self.target, self.android_module))
@@ -870,6 +882,8 @@ class AndroidMkWriter(object):
else:
self.WriteLn('LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp')
self.WriteLn('LOCAL_UNINSTALLABLE_MODULE := true')
if self.toolset == 'target':
self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX := $(GYP_VAR_PREFIX)')
self.WriteLn()
self.WriteLn('include $(BUILD_SYSTEM)/base_rules.mk')
self.WriteLn()
@@ -877,6 +891,9 @@ class AndroidMkWriter(object):
self.WriteLn('\t$(hide) echo "Gyp timestamp: $@"')
self.WriteLn('\t$(hide) mkdir -p $(dir $@)')
self.WriteLn('\t$(hide) touch $@')
if self.toolset == 'target':
self.WriteLn()
self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX :=')
def WriteList(self, value_list, variable=None, prefix='',
@@ -926,7 +943,7 @@ class AndroidMkWriter(object):
'INPUT_ROOT': expansion,
'INPUT_DIRNAME': dirname,
}
return path
return os.path.normpath(path)
def PerformBuild(data, configurations, params):
@@ -946,6 +963,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
generator_flags = params.get('generator_flags', {})
builddir_name = generator_flags.get('output_dir', 'out')
limit_to_target_all = generator_flags.get('limit_to_target_all', False)
write_alias_targets = generator_flags.get('write_alias_targets', True)
android_top_dir = os.environ.get('ANDROID_BUILD_TOP')
assert android_top_dir, '$ANDROID_BUILD_TOP not set; you need to run lunch.'
@@ -1041,7 +1059,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
writer = AndroidMkWriter(android_top_dir)
android_module = writer.Write(qualified_target, relative_target, base_path,
output_file, spec, configs,
part_of_all=part_of_all)
part_of_all=part_of_all,
write_alias_target=write_alias_targets)
if android_module in android_modules:
print ('ERROR: Android module names must be unique. The following '
'targets both generate Android module name %s.\n %s\n %s' %
@@ -1057,6 +1076,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
include_list.add(mkfile_rel_path)
root_makefile.write('GYP_CONFIGURATION ?= %s\n' % default_configuration)
root_makefile.write('GYP_VAR_PREFIX ?=\n')
# Write out the sorted list of includes.
root_makefile.write('\n')
@@ -1064,6 +1084,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
root_makefile.write('include $(LOCAL_PATH)/' + include_file + '\n')
root_makefile.write('\n')
root_makefile.write(SHARED_FOOTER)
if write_alias_targets:
root_makefile.write(ALL_MODULES_FOOTER)
root_makefile.close()

View File

@@ -165,7 +165,7 @@ def GetAllIncludeDirectories(target_list, target_dicts,
return all_includes_list
def GetCompilerPath(target_list, data):
def GetCompilerPath(target_list, data, options):
"""Determine a command that can be used to invoke the compiler.
Returns:
@@ -173,13 +173,12 @@ def GetCompilerPath(target_list, data):
the compiler from that. Otherwise, see if a compiler was specified via the
CC_target environment variable.
"""
# First, see if the compiler is configured in make's settings.
build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
make_global_settings_dict = data[build_file].get('make_global_settings', {})
for key, value in make_global_settings_dict:
if key in ['CC', 'CXX']:
return value
return os.path.join(options.toplevel_dir, value)
# Check to see if the compiler was specified as an environment variable.
for key in ['CC_target', 'CC', 'CXX']:
@@ -304,7 +303,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File',
'GNU C++', 'GNU C', 'Assembly']
compiler_path = GetCompilerPath(target_list, data)
compiler_path = GetCompilerPath(target_list, data, options)
include_dirs = GetAllIncludeDirectories(target_list, target_dicts,
shared_intermediate_dirs, config_name,
params, compiler_path)

View File

@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import collections
import copy
import ntpath
import os
@@ -21,6 +20,7 @@ import gyp.MSVSUserFile as MSVSUserFile
import gyp.MSVSUtil as MSVSUtil
import gyp.MSVSVersion as MSVSVersion
from gyp.common import GypError
from gyp.common import OrderedSet
# TODO: Remove once bots are on 2.7, http://crbug.com/241769
def _import_OrderedDict():
@@ -81,6 +81,7 @@ generator_additional_non_configuration_keys = [
'msvs_external_builder_out_dir',
'msvs_external_builder_build_cmd',
'msvs_external_builder_clean_cmd',
'msvs_external_builder_clcompile_cmd',
]
@@ -97,46 +98,6 @@ cached_username = None
cached_domain = None
# Based on http://code.activestate.com/recipes/576694/.
class OrderedSet(collections.MutableSet):
def __init__(self, iterable=None):
self.end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.map = {} # key --> [key, prev, next]
if iterable is not None:
self |= iterable
def __len__(self):
return len(self.map)
def discard(self, key):
if key in self.map:
key, prev, next = self.map.pop(key)
prev[2] = next
next[1] = prev
def __contains__(self, key):
return key in self.map
def add(self, key):
if key not in self.map:
end = self.end
curr = end[1]
curr[2] = end[1] = self.map[key] = [key, curr, end]
def update(self, iterable):
for i in iterable:
if i not in self:
self.add(i)
def __iter__(self):
end = self.end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
# TODO(gspencer): Switch the os.environ calls to be
# win32api.GetDomainName() and win32api.GetUserName() once the
# python version in depot_tools has been updated to work on Vista
@@ -858,17 +819,21 @@ def _GenerateRulesForMSVS(p, output_dir, options, spec,
def _AdjustSourcesForRules(spec, rules, sources, excluded_sources):
# Add outputs generated by each rule (if applicable).
for rule in rules:
# Done if not processing outputs as sources.
if int(rule.get('process_outputs_as_sources', False)):
# Add in the outputs from this rule.
trigger_files = _FindRuleTriggerFiles(rule, sources)
for trigger_file in trigger_files:
# Add in the outputs from this rule.
trigger_files = _FindRuleTriggerFiles(rule, sources)
for trigger_file in trigger_files:
# Remove trigger_file from excluded_sources to let the rule be triggered
# (e.g. rule trigger ax_enums.idl is added to excluded_sources
# because it's also in an action's inputs in the same project)
excluded_sources.discard(_FixPath(trigger_file))
# Done if not processing outputs as sources.
if int(rule.get('process_outputs_as_sources', False)):
inputs, outputs = _RuleInputsAndOutputs(rule, trigger_file)
inputs = OrderedSet(_FixPaths(inputs))
outputs = OrderedSet(_FixPaths(outputs))
inputs.remove(_FixPath(trigger_file))
sources.update(inputs)
if not spec.get('msvs_external_builder'):
if spec['type'] != 'none' and not spec.get('msvs_external_builder'):
excluded_sources.update(inputs)
sources.update(outputs)
@@ -1427,7 +1392,7 @@ def _PrepareListOfSources(spec, generator_flags, gyp_file):
# Add all inputs to sources and excluded sources.
inputs = OrderedSet(inputs)
sources.update(inputs)
if not spec.get('msvs_external_builder'):
if spec['type'] != 'none' and not spec.get('msvs_external_builder'):
excluded_sources.update(inputs)
if int(a.get('process_outputs_as_sources', False)):
_AddNormalizedSources(sources, a.get('outputs', []))
@@ -1479,8 +1444,14 @@ def _AdjustSourcesAndConvertToFilterHierarchy(
# Prune filters with a single child to flatten ugly directory structures
# such as ../../src/modules/module1 etc.
while len(sources) == 1 and isinstance(sources[0], MSVSProject.Filter):
sources = sources[0].contents
if version.UsesVcxproj():
while all([isinstance(s, MSVSProject.Filter) for s in sources]) \
and len(set([s.name for s in sources])) == 1:
assert all([len(s.contents) == 1 for s in sources])
sources = [s.contents[0] for s in sources]
else:
while len(sources) == 1 and isinstance(sources[0], MSVSProject.Filter):
sources = sources[0].contents
return sources, excluded_sources, excluded_idl
@@ -2584,6 +2555,7 @@ def _GetMSBuildGlobalProperties(spec, guid, gyp_file_name):
['ProjectGuid', guid],
['Keyword', 'Win32Proj'],
['RootNamespace', namespace],
['IgnoreWarnCompileDuplicatedFilename', 'true'],
]
]
@@ -3226,7 +3198,9 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
def _GetMSBuildExternalBuilderTargets(spec):
"""Return a list of MSBuild targets for external builders.
Right now, only "Build" and "Clean" targets are generated.
The "Build" and "Clean" targets are always generated. If the spec contains
'msvs_external_builder_clcompile_cmd', then the "ClCompile" target will also
be generated, to support building selected C/C++ files.
Arguments:
spec: The gyp target spec.
@@ -3245,7 +3219,17 @@ def _GetMSBuildExternalBuilderTargets(spec):
clean_target = ['Target', {'Name': 'Clean'}]
clean_target.append(['Exec', {'Command': clean_cmd}])
return [build_target, clean_target]
targets = [build_target, clean_target]
if spec.get('msvs_external_builder_clcompile_cmd'):
clcompile_cmd = _BuildCommandLineForRuleRaw(
spec, spec['msvs_external_builder_clcompile_cmd'],
False, False, False, False)
clcompile_target = ['Target', {'Name': 'ClCompile'}]
clcompile_target.append(['Exec', {'Command': clcompile_cmd}])
targets.append(clcompile_target)
return targets
def _GetMSBuildExtensions(props_files_of_rules):

View File

@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import collections
import copy
import hashlib
import json
@@ -343,7 +344,7 @@ class NinjaWriter:
return os.path.normpath(os.path.join(obj, self.base_dir, path_dir,
path_basename))
def WriteCollapsedDependencies(self, name, targets):
def WriteCollapsedDependencies(self, name, targets, order_only=None):
"""Given a list of targets, return a path for a single file
representing the result of building all the targets or None.
@@ -351,10 +352,11 @@ class NinjaWriter:
assert targets == filter(None, targets), targets
if len(targets) == 0:
assert not order_only
return None
if len(targets) > 1:
if len(targets) > 1 or order_only:
stamp = self.GypPathToUniqueOutput(name + '.stamp')
targets = self.ninja.build(stamp, 'stamp', targets)
targets = self.ninja.build(stamp, 'stamp', targets, order_only=order_only)
self.ninja.newline()
return targets[0]
@@ -472,6 +474,8 @@ class NinjaWriter:
else:
print "Warning: Actions/rules writing object files don't work with " \
"multiarch targets, dropping. (target %s)" % spec['target_name']
elif self.flavor == 'mac' and len(self.archs) > 1:
link_deps = collections.defaultdict(list)
if self.flavor == 'win' and self.target.type == 'static_library':
@@ -580,10 +584,7 @@ class NinjaWriter:
def WriteActions(self, actions, extra_sources, prebuild,
extra_mac_bundle_resources):
# Actions cd into the base directory.
env = self.GetSortedXcodeEnv()
if self.flavor == 'win':
env = self.msvs_settings.GetVSMacroEnv(
'$!PRODUCT_DIR', config=self.config_name)
env = self.GetToolchainEnv()
all_outputs = []
for action in actions:
# First write out a rule for the action.
@@ -616,15 +617,17 @@ class NinjaWriter:
def WriteRules(self, rules, extra_sources, prebuild,
mac_bundle_resources, extra_mac_bundle_resources):
env = self.GetSortedXcodeEnv()
env = self.GetToolchainEnv()
all_outputs = []
for rule in rules:
# First write out a rule for the rule action.
name = '%s_%s' % (rule['rule_name'],
hashlib.md5(self.qualified_target).hexdigest())
# Skip a rule with no action and no inputs.
if 'action' not in rule and not rule.get('rule_sources', []):
continue
# First write out a rule for the rule action.
name = '%s_%s' % (rule['rule_name'],
hashlib.md5(self.qualified_target).hexdigest())
args = rule['action']
description = self.GenerateDescription(
'RULE',
@@ -653,8 +656,22 @@ class NinjaWriter:
return path.replace('\\', '/')
return path
inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])]
# If there are n source files matching the rule, and m additional rule
# inputs, then adding 'inputs' to each build edge written below will
# write m * n inputs. Collapsing reduces this to m + n.
sources = rule.get('rule_sources', [])
num_inputs = len(inputs)
if prebuild:
num_inputs += 1
if num_inputs > 2 and len(sources) > 2:
inputs = [self.WriteCollapsedDependencies(
rule['rule_name'], inputs, order_only=prebuild)]
prebuild = []
# For each source file, write an edge that generates all the outputs.
for source in rule.get('rule_sources', []):
for source in sources:
source = os.path.normpath(source)
dirname, basename = os.path.split(source)
root, ext = os.path.splitext(basename)
@@ -663,9 +680,6 @@ class NinjaWriter:
outputs = [self.ExpandRuleVariables(o, root, dirname,
source, ext, basename)
for o in rule['outputs']]
inputs = [self.ExpandRuleVariables(i, root, dirname,
source, ext, basename)
for i in rule.get('inputs', [])]
if int(rule.get('process_outputs_as_sources', False)):
extra_sources += outputs
@@ -703,10 +717,11 @@ class NinjaWriter:
else:
assert var == None, repr(var)
inputs = [self.GypPathToNinja(i, env) for i in inputs]
outputs = [self.GypPathToNinja(o, env) for o in outputs]
extra_bindings.append(('unique_name',
hashlib.md5(outputs[0]).hexdigest()))
if self.flavor == 'win':
# WriteNewNinjaRule uses unique_name for creating an rsp file on win.
extra_bindings.append(('unique_name',
hashlib.md5(outputs[0]).hexdigest()))
self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
implicit=inputs,
order_only=prebuild,
@@ -718,7 +733,7 @@ class NinjaWriter:
def WriteCopies(self, copies, prebuild, mac_bundle_depends):
outputs = []
env = self.GetSortedXcodeEnv()
env = self.GetToolchainEnv()
for copy in copies:
for path in copy['files']:
# Normalize the path so trailing slashes don't confuse us.
@@ -810,6 +825,7 @@ class NinjaWriter:
cflags_objcc = ['$cflags_cc'] + \
self.xcode_settings.GetCflagsObjCC(config_name)
elif self.flavor == 'win':
asmflags = self.msvs_settings.GetAsmflags(config_name)
cflags = self.msvs_settings.GetCflags(config_name)
cflags_c = self.msvs_settings.GetCflagsC(config_name)
cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
@@ -844,16 +860,17 @@ class NinjaWriter:
self.WriteVariableList(ninja_file, 'defines',
[Define(d, self.flavor) for d in defines])
if self.flavor == 'win':
self.WriteVariableList(ninja_file, 'asmflags',
map(self.ExpandSpecial, asmflags))
self.WriteVariableList(ninja_file, 'rcflags',
[QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
for f in self.msvs_settings.GetRcflags(config_name,
self.GypPathToNinja)])
include_dirs = config.get('include_dirs', [])
env = self.GetSortedXcodeEnv()
env = self.GetToolchainEnv()
if self.flavor == 'win':
env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
config=config_name)
include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
config_name)
self.WriteVariableList(ninja_file, 'includes',
@@ -1095,6 +1112,23 @@ class NinjaWriter:
extra_bindings.append(('soname', os.path.split(output)[1]))
extra_bindings.append(('lib',
gyp.common.EncodePOSIXShellArgument(output)))
if self.flavor != 'win':
link_file_list = output
if self.is_mac_bundle:
# 'Dependency Framework.framework/Versions/A/Dependency Framework' ->
# 'Dependency Framework.framework.rsp'
link_file_list = self.xcode_settings.GetWrapperName()
if arch:
link_file_list += '.' + arch
link_file_list += '.rsp'
# If an rspfile contains spaces, ninja surrounds the filename with
# quotes around it and then passes it to open(), creating a file with
# quotes in its name (and when looking for the rsp file, the name
# makes it through bash which strips the quotes) :-/
link_file_list = link_file_list.replace(' ', '_')
extra_bindings.append(
('link_file_list',
gyp.common.EncodePOSIXShellArgument(link_file_list)))
if self.flavor == 'win':
extra_bindings.append(('binary', output))
if '/NOENTRY' not in ldflags:
@@ -1196,6 +1230,19 @@ class NinjaWriter:
self.target.bundle = output
return output
def GetToolchainEnv(self, additional_settings=None):
"""Returns the variables toolchain would set for build steps."""
env = self.GetSortedXcodeEnv(additional_settings=additional_settings)
if self.flavor == 'win':
env = self.GetMsvsToolchainEnv(
additional_settings=additional_settings)
return env
def GetMsvsToolchainEnv(self, additional_settings=None):
"""Returns the variables Visual Studio would set for build steps."""
return self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
config=self.config_name)
def GetSortedXcodeEnv(self, additional_settings=None):
"""Returns the variables Xcode would set for build steps."""
assert self.abs_build_dir
@@ -1559,14 +1606,15 @@ def GetDefaultConcurrentLinks():
hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
return min(mem_limit, hard_cap)
elif sys.platform.startswith('linux'):
with open("/proc/meminfo") as meminfo:
memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
for line in meminfo:
match = memtotal_re.match(line)
if not match:
continue
# Allow 8Gb per link on Linux because Gold is quite memory hungry
return max(1, int(match.group(1)) / (8 * (2 ** 20)))
if os.path.exists("/proc/meminfo"):
with open("/proc/meminfo") as meminfo:
memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
for line in meminfo:
match = memtotal_re.match(line)
if not match:
continue
# Allow 8Gb per link on Linux because Gold is quite memory hungry
return max(1, int(match.group(1)) / (8 * (2 ** 20)))
return 1
elif sys.platform == 'darwin':
try:
@@ -1842,9 +1890,9 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
sys.executable))
master_ninja.rule(
'asm',
description='ASM $in',
description='ASM $out',
command=('%s gyp-win-tool asm-wrapper '
'$arch $asm $defines $includes /c /Fo $out $in' %
'$arch $asm $defines $includes $asmflags /c /Fo $out $in' %
sys.executable))
if flavor != 'mac' and flavor != 'win':
@@ -1863,32 +1911,33 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
# The resulting string leaves an uninterpolated %{suffix} which
# is used in the final substitution below.
mtime_preserving_solink_base = (
'if [ ! -e $lib -o ! -e ${lib}.TOC ]; then '
'%(solink)s && %(extract_toc)s > ${lib}.TOC; else '
'%(solink)s && %(extract_toc)s > ${lib}.tmp && '
'if ! cmp -s ${lib}.tmp ${lib}.TOC; then mv ${lib}.tmp ${lib}.TOC ; '
'if [ ! -e $lib -o ! -e $lib.TOC ]; then '
'%(solink)s && %(extract_toc)s > $lib.TOC; else '
'%(solink)s && %(extract_toc)s > $lib.tmp && '
'if ! cmp -s $lib.tmp $lib.TOC; then mv $lib.tmp $lib.TOC ; '
'fi; fi'
% { 'solink':
'$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s',
'extract_toc':
('{ readelf -d ${lib} | grep SONAME ; '
'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')})
('{ readelf -d $lib | grep SONAME ; '
'nm -gD -f p $lib | cut -f1-2 -d\' \'; }')})
master_ninja.rule(
'solink',
description='SOLINK $lib',
restat=True,
command=(mtime_preserving_solink_base % {
'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
'$libs'}),
command=mtime_preserving_solink_base % {'suffix': '@$link_file_list'},
rspfile='$link_file_list',
rspfile_content=
'-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive $libs',
pool='link_pool')
master_ninja.rule(
'solink_module',
description='SOLINK(module) $lib',
restat=True,
command=(mtime_preserving_solink_base % {
'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group '
'$libs'}),
command=mtime_preserving_solink_base % {'suffix': '@$link_file_list'},
rspfile='$link_file_list',
rspfile_content='-Wl,--start-group $in $solibs -Wl,--end-group $libs',
pool='link_pool')
master_ninja.rule(
'link',
@@ -1938,16 +1987,16 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
# comment in the posix section above for details.
solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
mtime_preserving_solink_base = (
'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
'if [ ! -e $lib -o ! -e $lib.TOC ] || '
# Always force dependent targets to relink if this library
# reexports something. Handling this correctly would require
# recursive TOC dumping but this is rare in practice, so punt.
'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
'%(solink)s && %(extract_toc)s > ${lib}.TOC; '
'%(solink)s && %(extract_toc)s > $lib.TOC; '
'else '
'%(solink)s && %(extract_toc)s > ${lib}.tmp && '
'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
'mv ${lib}.tmp ${lib}.TOC ; '
'%(solink)s && %(extract_toc)s > $lib.tmp && '
'if ! cmp -s $lib.tmp $lib.TOC; then '
'mv $lib.tmp $lib.TOC ; '
'fi; '
'fi'
% { 'solink': solink_base,
@@ -1955,34 +2004,42 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
'{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
solink_suffix = '$in $solibs $libs$postbuilds'
solink_suffix = '@$link_file_list$postbuilds'
master_ninja.rule(
'solink',
description='SOLINK $lib, POSTBUILDS',
restat=True,
command=mtime_preserving_solink_base % {'suffix': solink_suffix,
'type': '-shared'},
rspfile='$link_file_list',
rspfile_content='$in $solibs $libs',
pool='link_pool')
master_ninja.rule(
'solink_notoc',
description='SOLINK $lib, POSTBUILDS',
restat=True,
command=solink_base % {'suffix':solink_suffix, 'type': '-shared'},
rspfile='$link_file_list',
rspfile_content='$in $solibs $libs',
pool='link_pool')
solink_module_suffix = '$in $solibs $libs$postbuilds'
master_ninja.rule(
'solink_module',
description='SOLINK(module) $lib, POSTBUILDS',
restat=True,
command=mtime_preserving_solink_base % {'suffix': solink_module_suffix,
command=mtime_preserving_solink_base % {'suffix': solink_suffix,
'type': '-bundle'},
rspfile='$link_file_list',
rspfile_content='$in $solibs $libs',
pool='link_pool')
master_ninja.rule(
'solink_module_notoc',
description='SOLINK(module) $lib, POSTBUILDS',
restat=True,
command=solink_base % {'suffix': solink_module_suffix, 'type': '-bundle'},
command=solink_base % {'suffix': solink_suffix, 'type': '-bundle'},
rspfile='$link_file_list',
rspfile_content='$in $solibs $libs',
pool='link_pool')
master_ninja.rule(

View File

@@ -5,6 +5,7 @@
import filecmp
import gyp.common
import gyp.xcodeproj_file
import gyp.xcode_ninja
import errno
import os
import sys
@@ -575,6 +576,12 @@ def PerformBuild(data, configurations, params):
def GenerateOutput(target_list, target_dicts, data, params):
# Optionally configure each spec to use ninja as the external builder.
ninja_wrapper = params.get('flavor') == 'ninja'
if ninja_wrapper:
(target_list, target_dicts, data) = \
gyp.xcode_ninja.CreateWrapper(target_list, target_dicts, data, params)
options = params['options']
generator_flags = params.get('generator_flags', {})
parallel_builds = generator_flags.get('xcode_parallel_builds', True)
@@ -703,11 +710,16 @@ def GenerateOutput(target_list, target_dicts, data, params):
# and is made a dependency of this target. This way the work is done
# before the dependency checks for what should be recompiled.
support_xct = None
if type != 'none' and (spec_actions or spec_rules):
# The Xcode "issues" don't affect xcode-ninja builds, since the dependency
# logic all happens in ninja. Don't bother creating the extra targets in
# that case.
if type != 'none' and (spec_actions or spec_rules) and not ninja_wrapper:
support_xccl = CreateXCConfigurationList(configuration_names);
support_target_suffix = generator_flags.get(
'support_target_suffix', ' Support')
support_target_properties = {
'buildConfigurationList': support_xccl,
'name': target_name + ' Support',
'name': target_name + support_target_suffix,
}
if target_product_name:
support_target_properties['productName'] = \

View File

@@ -10,8 +10,8 @@ from compiler.ast import Module
from compiler.ast import Node
from compiler.ast import Stmt
import compiler
import copy
import gyp.common
import gyp.simple_copy
import multiprocessing
import optparse
import os.path
@@ -24,6 +24,7 @@ import threading
import time
import traceback
from gyp.common import GypError
from gyp.common import OrderedSet
# A list of types that are treated as linkable.
@@ -45,18 +46,31 @@ base_path_sections = [
'outputs',
'sources',
]
path_sections = []
is_path_section_charset = set('=+?!')
is_path_section_match_re = re.compile('_(dir|file|path)s?$')
path_sections = set()
def IsPathSection(section):
# If section ends in one of these characters, it's applied to a section
# If section ends in one of the '=+?!' characters, it's applied to a section
# without the trailing characters. '/' is notably absent from this list,
# because there's no way for a regular expression to be treated as a path.
while section[-1:] in is_path_section_charset:
while section[-1:] in '=+?!':
section = section[:-1]
return section in path_sections or is_path_section_match_re.search(section)
if section in path_sections:
return True
# Sections mathing the regexp '_(dir|file|path)s?$' are also
# considered PathSections. Using manual string matching since that
# is much faster than the regexp and this can be called hundreds of
# thousands of times so micro performance matters.
if "_" in section:
tail = section[-6:]
if tail[-1] == 's':
tail = tail[:-1]
if tail[-5:] in ('_file', '_path'):
return True
return tail[-4:] == '_dir'
return False
# base_non_configuration_keys is a list of key names that belong in the target
# itself and should not be propagated into its configurations. It is merged
@@ -200,7 +214,7 @@ def CheckNode(node, keypath):
"': " + repr(node)
def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
def LoadOneBuildFile(build_file_path, data, aux_data, includes,
is_target, check):
if build_file_path in data:
return data[build_file_path]
@@ -224,7 +238,7 @@ def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
gyp.common.ExceptionAppend(e, 'while reading ' + build_file_path)
raise
if not isinstance(build_file_data, dict):
if type(build_file_data) is not dict:
raise GypError("%s does not evaluate to a dictionary." % build_file_path)
data[build_file_path] = build_file_data
@@ -236,10 +250,10 @@ def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
try:
if is_target:
LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data,
aux_data, variables, includes, check)
aux_data, includes, check)
else:
LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data,
aux_data, variables, None, check)
aux_data, None, check)
except Exception, e:
gyp.common.ExceptionAppend(e,
'while reading includes of ' + build_file_path)
@@ -249,7 +263,7 @@ def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
def LoadBuildFileIncludesIntoDict(subdict, subdict_path, data, aux_data,
variables, includes, check):
includes, check):
includes_list = []
if includes != None:
includes_list.extend(includes)
@@ -273,30 +287,27 @@ def LoadBuildFileIncludesIntoDict(subdict, subdict_path, data, aux_data,
gyp.DebugOutput(gyp.DEBUG_INCLUDES, "Loading Included File: '%s'", include)
MergeDicts(subdict,
LoadOneBuildFile(include, data, aux_data, variables, None,
False, check),
LoadOneBuildFile(include, data, aux_data, None, False, check),
subdict_path, include)
# Recurse into subdictionaries.
for k, v in subdict.iteritems():
if v.__class__ == dict:
LoadBuildFileIncludesIntoDict(v, subdict_path, data, aux_data, variables,
if type(v) is dict:
LoadBuildFileIncludesIntoDict(v, subdict_path, data, aux_data,
None, check)
elif v.__class__ == list:
LoadBuildFileIncludesIntoList(v, subdict_path, data, aux_data, variables,
elif type(v) is list:
LoadBuildFileIncludesIntoList(v, subdict_path, data, aux_data,
check)
# This recurses into lists so that it can look for dicts.
def LoadBuildFileIncludesIntoList(sublist, sublist_path, data, aux_data,
variables, check):
def LoadBuildFileIncludesIntoList(sublist, sublist_path, data, aux_data, check):
for item in sublist:
if item.__class__ == dict:
if type(item) is dict:
LoadBuildFileIncludesIntoDict(item, sublist_path, data, aux_data,
variables, None, check)
elif item.__class__ == list:
LoadBuildFileIncludesIntoList(item, sublist_path, data, aux_data,
variables, check)
None, check)
elif type(item) is list:
LoadBuildFileIncludesIntoList(item, sublist_path, data, aux_data, check)
# Processes toolsets in all the targets. This recurses into condition entries
# since they can contain toolsets as well.
@@ -320,7 +331,7 @@ def ProcessToolsetsInDict(data):
if len(toolsets) > 0:
# Optimization: only do copies if more than one toolset is specified.
for build in toolsets[1:]:
new_target = copy.deepcopy(target)
new_target = gyp.simple_copy.deepcopy(target)
new_target['toolset'] = build
new_target_list.append(new_target)
target['toolset'] = toolsets[0]
@@ -328,7 +339,7 @@ def ProcessToolsetsInDict(data):
data['targets'] = new_target_list
if 'conditions' in data:
for condition in data['conditions']:
if isinstance(condition, list):
if type(condition) is list:
for condition_dict in condition[1:]:
ProcessToolsetsInDict(condition_dict)
@@ -358,7 +369,7 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
gyp.DebugOutput(gyp.DEBUG_INCLUDES,
"Loading Target Build File '%s'", build_file_path)
build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, variables,
build_file_data = LoadOneBuildFile(build_file_path, data, aux_data,
includes, True, check)
# Store DEPTH for later use in generators.
@@ -408,7 +419,8 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
# copy with the target-specific data merged into it as the replacement
# target dict.
old_target_dict = build_file_data['targets'][index]
new_target_dict = copy.deepcopy(build_file_data['target_defaults'])
new_target_dict = gyp.simple_copy.deepcopy(
build_file_data['target_defaults'])
MergeDicts(new_target_dict, old_target_dict,
build_file_path, build_file_path)
build_file_data['targets'][index] = new_target_dict
@@ -636,15 +648,26 @@ def FindEnclosingBracketGroup(input_str):
return (-1, -1)
canonical_int_re = re.compile('(0|-?[1-9][0-9]*)$')
def IsStrCanonicalInt(string):
"""Returns True if |string| is in its canonical integer form.
The canonical form is such that str(int(string)) == string.
"""
return isinstance(string, str) and canonical_int_re.match(string)
if type(string) is str:
# This function is called a lot so for maximum performance, avoid
# involving regexps which would otherwise make the code much
# shorter. Regexps would need twice the time of this function.
if string:
if string == "0":
return True
if string[0] == "-":
string = string[1:]
if not string:
return False
if '1' <= string[0] <= '9':
return string.isdigit()
return False
# This matches things like "<(asdf)", "<!(cmd)", "<!@(cmd)", "<|(list)",
@@ -677,7 +700,7 @@ cached_command_results = {}
def FixupPlatformCommand(cmd):
if sys.platform == 'win32':
if type(cmd) == list:
if type(cmd) is list:
cmd = [re.sub('^cat ', 'type ', cmd[0])] + cmd[1:]
else:
cmd = re.sub('^cat ', 'type ', cmd)
@@ -767,7 +790,7 @@ def ExpandVariables(input, phase, variables, build_file):
# contexts. However, since filtration has no chance to run on <|(),
# this seems like the only obvious way to give them access to filters.
if file_list:
processed_variables = copy.deepcopy(variables)
processed_variables = gyp.simple_copy.deepcopy(variables)
ProcessListFiltersInDict(contents, processed_variables)
# Recurse to expand variables in the contents
contents = ExpandVariables(contents, phase,
@@ -804,7 +827,7 @@ def ExpandVariables(input, phase, variables, build_file):
# This works around actions/rules which have more inputs than will
# fit on the command line.
if file_list:
if type(contents) == list:
if type(contents) is list:
contents_list = contents
else:
contents_list = contents.split(' ')
@@ -837,17 +860,15 @@ def ExpandVariables(input, phase, variables, build_file):
use_shell = False
# Check for a cached value to avoid executing commands, or generating
# file lists more than once.
# TODO(http://code.google.com/p/gyp/issues/detail?id=112): It is
# possible that the command being invoked depends on the current
# directory. For that case the syntax needs to be extended so that the
# directory is also used in cache_key (it becomes a tuple).
# file lists more than once. The cache key contains the command to be
# run as well as the directory to run it from, to account for commands
# that depend on their current directory.
# TODO(http://code.google.com/p/gyp/issues/detail?id=111): In theory,
# someone could author a set of GYP files where each time the command
# is invoked it produces different output by design. When the need
# arises, the syntax should be extended to support no caching off a
# command's output so it is run every time.
cache_key = str(contents)
cache_key = (str(contents), build_file_dir)
cached_value = cached_command_results.get(cache_key, None)
if cached_value is None:
gyp.DebugOutput(gyp.DEBUG_VARIABLES,
@@ -925,10 +946,9 @@ def ExpandVariables(input, phase, variables, build_file):
else:
replacement = variables[contents]
if isinstance(replacement, list):
if type(replacement) is list:
for item in replacement:
if (not contents[-1] == '/' and
not isinstance(item, str) and not isinstance(item, int)):
if not contents[-1] == '/' and type(item) not in (str, int):
raise GypError('Variable ' + contents +
' must expand to a string or list of strings; ' +
'list contains a ' +
@@ -938,8 +958,7 @@ def ExpandVariables(input, phase, variables, build_file):
# with conditions sections.
ProcessVariablesAndConditionsInList(replacement, phase, variables,
build_file)
elif not isinstance(replacement, str) and \
not isinstance(replacement, int):
elif type(replacement) not in (str, int):
raise GypError('Variable ' + contents +
' must expand to a string or list of strings; ' +
'found a ' + replacement.__class__.__name__)
@@ -948,7 +967,7 @@ def ExpandVariables(input, phase, variables, build_file):
# Expanding in list context. It's guaranteed that there's only one
# replacement to do in |input_str| and that it's this replacement. See
# above.
if isinstance(replacement, list):
if type(replacement) is list:
# If it's already a list, make a copy.
output = replacement[:]
else:
@@ -957,7 +976,7 @@ def ExpandVariables(input, phase, variables, build_file):
else:
# Expanding in string context.
encoded_replacement = ''
if isinstance(replacement, list):
if type(replacement) is list:
# When expanding a list into string context, turn the list items
# into a string in a way that will work with a subprocess call.
#
@@ -979,8 +998,8 @@ def ExpandVariables(input, phase, variables, build_file):
# expanding local variables (variables defined in the same
# variables block as this one).
gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found output %r, recursing.", output)
if isinstance(output, list):
if output and isinstance(output[0], list):
if type(output) is list:
if output and type(output[0]) is list:
# Leave output alone if it's a list of lists.
# We don't want such lists to be stringified.
pass
@@ -994,7 +1013,7 @@ def ExpandVariables(input, phase, variables, build_file):
output = ExpandVariables(output, phase, variables, build_file)
# Convert all strings that are canonically-represented integers into integers.
if isinstance(output, list):
if type(output) is list:
for index in xrange(0, len(output)):
if IsStrCanonicalInt(output[index]):
output[index] = int(output[index])
@@ -1003,6 +1022,57 @@ def ExpandVariables(input, phase, variables, build_file):
return output
# The same condition is often evaluated over and over again so it
# makes sense to cache as much as possible between evaluations.
cached_conditions_asts = {}
def EvalCondition(condition, conditions_key, phase, variables, build_file):
"""Returns the dict that should be used or None if the result was
that nothing should be used."""
if type(condition) is not list:
raise GypError(conditions_key + ' must be a list')
if len(condition) != 2 and len(condition) != 3:
# It's possible that condition[0] won't work in which case this
# attempt will raise its own IndexError. That's probably fine.
raise GypError(conditions_key + ' ' + condition[0] +
' must be length 2 or 3, not ' + str(len(condition)))
[cond_expr, true_dict] = condition[0:2]
false_dict = None
if len(condition) == 3:
false_dict = condition[2]
# Do expansions on the condition itself. Since the conditon can naturally
# contain variable references without needing to resort to GYP expansion
# syntax, this is of dubious value for variables, but someone might want to
# use a command expansion directly inside a condition.
cond_expr_expanded = ExpandVariables(cond_expr, phase, variables,
build_file)
if type(cond_expr_expanded) not in (str, int):
raise ValueError, \
'Variable expansion in this context permits str and int ' + \
'only, found ' + cond_expr_expanded.__class__.__name__
try:
if cond_expr_expanded in cached_conditions_asts:
ast_code = cached_conditions_asts[cond_expr_expanded]
else:
ast_code = compile(cond_expr_expanded, '<string>', 'eval')
cached_conditions_asts[cond_expr_expanded] = ast_code
if eval(ast_code, {'__builtins__': None}, variables):
return true_dict
return false_dict
except SyntaxError, e:
syntax_error = SyntaxError('%s while evaluating condition \'%s\' in %s '
'at character %d.' %
(str(e.args[0]), e.text, build_file, e.offset),
e.filename, e.lineno, e.offset, e.text)
raise syntax_error
except NameError, e:
gyp.common.ExceptionAppend(e, 'while evaluating condition \'%s\' in %s' %
(cond_expr_expanded, build_file))
raise GypError(e)
def ProcessConditionsInDict(the_dict, phase, variables, build_file):
# Process a 'conditions' or 'target_conditions' section in the_dict,
@@ -1038,48 +1108,8 @@ def ProcessConditionsInDict(the_dict, phase, variables, build_file):
del the_dict[conditions_key]
for condition in conditions_list:
if not isinstance(condition, list):
raise GypError(conditions_key + ' must be a list')
if len(condition) != 2 and len(condition) != 3:
# It's possible that condition[0] won't work in which case this
# attempt will raise its own IndexError. That's probably fine.
raise GypError(conditions_key + ' ' + condition[0] +
' must be length 2 or 3, not ' + str(len(condition)))
[cond_expr, true_dict] = condition[0:2]
false_dict = None
if len(condition) == 3:
false_dict = condition[2]
# Do expansions on the condition itself. Since the conditon can naturally
# contain variable references without needing to resort to GYP expansion
# syntax, this is of dubious value for variables, but someone might want to
# use a command expansion directly inside a condition.
cond_expr_expanded = ExpandVariables(cond_expr, phase, variables,
build_file)
if not isinstance(cond_expr_expanded, str) and \
not isinstance(cond_expr_expanded, int):
raise ValueError, \
'Variable expansion in this context permits str and int ' + \
'only, found ' + expanded.__class__.__name__
try:
ast_code = compile(cond_expr_expanded, '<string>', 'eval')
if eval(ast_code, {'__builtins__': None}, variables):
merge_dict = true_dict
else:
merge_dict = false_dict
except SyntaxError, e:
syntax_error = SyntaxError('%s while evaluating condition \'%s\' in %s '
'at character %d.' %
(str(e.args[0]), e.text, build_file, e.offset),
e.filename, e.lineno, e.offset, e.text)
raise syntax_error
except NameError, e:
gyp.common.ExceptionAppend(e, 'while evaluating condition \'%s\' in %s' %
(cond_expr_expanded, build_file))
raise GypError(e)
merge_dict = EvalCondition(condition, conditions_key, phase, variables,
build_file)
if merge_dict != None:
# Expand variables and nested conditinals in the merge_dict before
@@ -1094,8 +1124,7 @@ def LoadAutomaticVariablesFromDict(variables, the_dict):
# Any keys with plain string values in the_dict become automatic variables.
# The variable name is the key name with a "_" character prepended.
for key, value in the_dict.iteritems():
if isinstance(value, str) or isinstance(value, int) or \
isinstance(value, list):
if type(value) in (str, int, list):
variables['_' + key] = value
@@ -1108,8 +1137,7 @@ def LoadVariablesFromVariablesDict(variables, the_dict, the_dict_key):
# (it could be a list or it could be parentless because it is a root dict),
# the_dict_key will be None.
for key, value in the_dict.get('variables', {}).iteritems():
if not isinstance(value, str) and not isinstance(value, int) and \
not isinstance(value, list):
if type(value) not in (str, int, list):
continue
if key.endswith('%'):
@@ -1162,9 +1190,9 @@ def ProcessVariablesAndConditionsInDict(the_dict, phase, variables_in,
for key, value in the_dict.iteritems():
# Skip "variables", which was already processed if present.
if key != 'variables' and isinstance(value, str):
if key != 'variables' and type(value) is str:
expanded = ExpandVariables(value, phase, variables, build_file)
if not isinstance(expanded, str) and not isinstance(expanded, int):
if type(expanded) not in (str, int):
raise ValueError, \
'Variable expansion in this context permits str and int ' + \
'only, found ' + expanded.__class__.__name__ + ' for ' + key
@@ -1221,21 +1249,21 @@ def ProcessVariablesAndConditionsInDict(the_dict, phase, variables_in,
for key, value in the_dict.iteritems():
# Skip "variables" and string values, which were already processed if
# present.
if key == 'variables' or isinstance(value, str):
if key == 'variables' or type(value) is str:
continue
if isinstance(value, dict):
if type(value) is dict:
# Pass a copy of the variables dict so that subdicts can't influence
# parents.
ProcessVariablesAndConditionsInDict(value, phase, variables,
build_file, key)
elif isinstance(value, list):
elif type(value) is list:
# The list itself can't influence the variables dict, and
# ProcessVariablesAndConditionsInList will make copies of the variables
# dict if it needs to pass it to something that can influence it. No
# copy is necessary here.
ProcessVariablesAndConditionsInList(value, phase, variables,
build_file)
elif not isinstance(value, int):
elif type(value) is not int:
raise TypeError, 'Unknown type ' + value.__class__.__name__ + \
' for ' + key
@@ -1246,17 +1274,17 @@ def ProcessVariablesAndConditionsInList(the_list, phase, variables,
index = 0
while index < len(the_list):
item = the_list[index]
if isinstance(item, dict):
if type(item) is dict:
# Make a copy of the variables dict so that it won't influence anything
# outside of its own scope.
ProcessVariablesAndConditionsInDict(item, phase, variables, build_file)
elif isinstance(item, list):
elif type(item) is list:
ProcessVariablesAndConditionsInList(item, phase, variables, build_file)
elif isinstance(item, str):
elif type(item) is str:
expanded = ExpandVariables(item, phase, variables, build_file)
if isinstance(expanded, str) or isinstance(expanded, int):
if type(expanded) in (str, int):
the_list[index] = expanded
elif isinstance(expanded, list):
elif type(expanded) is list:
the_list[index:index+1] = expanded
index += len(expanded)
@@ -1268,7 +1296,7 @@ def ProcessVariablesAndConditionsInList(the_list, phase, variables,
'Variable expansion in this context permits strings and ' + \
'lists only, found ' + expanded.__class__.__name__ + ' at ' + \
index
elif not isinstance(item, int):
elif type(item) is not int:
raise TypeError, 'Unknown type ' + item.__class__.__name__ + \
' at index ' + index
index = index + 1
@@ -1443,6 +1471,20 @@ def RemoveSelfDependencies(targets):
target_dict[dependency_key] = Filter(dependencies, target_name)
def RemoveLinkDependenciesFromNoneTargets(targets):
"""Remove dependencies having the 'link_dependency' attribute from the 'none'
targets."""
for target_name, target_dict in targets.iteritems():
for dependency_key in dependency_sections:
dependencies = target_dict.get(dependency_key, [])
if dependencies:
for t in dependencies:
if target_dict.get('type', None) == 'none':
if targets[t].get('variables', {}).get('link_dependency', 0):
target_dict[dependency_key] = \
Filter(target_dict[dependency_key], t)
class DependencyGraphNode(object):
"""
@@ -1468,7 +1510,7 @@ class DependencyGraphNode(object):
# are the "ref" attributes of DependencyGraphNodes. Every target will
# appear in flat_list after all of its dependencies, and before all of its
# dependents.
flat_list = []
flat_list = OrderedSet()
# in_degree_zeros is the list of DependencyGraphNodes that have no
# dependencies not in flat_list. Initially, it is a copy of the children
@@ -1482,12 +1524,15 @@ class DependencyGraphNode(object):
# as work progresses, so that the next node to process from the list can
# always be accessed at a consistent position.
node = in_degree_zeros.pop()
flat_list.append(node.ref)
flat_list.add(node.ref)
# Look at dependents of the node just added to flat_list. Some of them
# may now belong in in_degree_zeros.
for node_dependent in node.dependents:
is_in_degree_zero = True
# TODO: We want to check through the
# node_dependent.dependencies list but if it's long and we
# always start at the beginning, then we get O(n^2) behaviour.
for node_dependent_dependency in node_dependent.dependencies:
if not node_dependent_dependency.ref in flat_list:
# The dependent one or more dependencies not in flat_list. There
@@ -1503,7 +1548,7 @@ class DependencyGraphNode(object):
# iteration of the outer loop.
in_degree_zeros.add(node_dependent)
return flat_list
return list(flat_list)
def FindCycles(self, path=None):
"""
@@ -1589,21 +1634,26 @@ class DependencyGraphNode(object):
return self._AddImportedDependencies(targets, dependencies)
def DeepDependencies(self, dependencies=None):
"""Returns a list of all of a target's dependencies, recursively."""
if dependencies == None:
dependencies = []
"""Returns an OrderedSet of all of a target's dependencies, recursively."""
if dependencies is None:
# Using a list to get ordered output and a set to do fast "is it
# already added" checks.
dependencies = OrderedSet()
for dependency in self.dependencies:
# Check for None, corresponding to the root node.
if dependency.ref != None and dependency.ref not in dependencies:
dependencies.append(dependency.ref)
if dependency.ref is None:
continue
if dependency.ref not in dependencies:
dependencies.add(dependency.ref)
dependency.DeepDependencies(dependencies)
return dependencies
def _LinkDependenciesInternal(self, targets, include_shared_libraries,
dependencies=None, initial=True):
"""Returns a list of dependency targets that are linked into this target.
"""Returns an OrderedSet of dependency targets that are linked
into this target.
This function has a split personality, depending on the setting of
|initial|. Outside callers should always leave |initial| at its default
@@ -1616,11 +1666,13 @@ class DependencyGraphNode(object):
If |include_shared_libraries| is False, the resulting dependencies will not
include shared_library targets that are linked into this target.
"""
if dependencies == None:
dependencies = []
if dependencies is None:
# Using a list to get ordered output and a set to do fast "is it
# already added" checks.
dependencies = OrderedSet()
# Check for None, corresponding to the root node.
if self.ref == None:
if self.ref is None:
return dependencies
# It's kind of sucky that |targets| has to be passed into this function,
@@ -1648,8 +1700,7 @@ class DependencyGraphNode(object):
# Don't traverse 'none' targets if explicitly excluded.
if (target_type == 'none' and
not targets[self.ref].get('dependencies_traverse', True)):
if self.ref not in dependencies:
dependencies.append(self.ref)
dependencies.add(self.ref)
return dependencies
# Executables and loadable modules are already fully and finally linked.
@@ -1671,7 +1722,7 @@ class DependencyGraphNode(object):
# The target is linkable, add it to the list of link dependencies.
if self.ref not in dependencies:
dependencies.append(self.ref)
dependencies.add(self.ref)
if initial or not is_linkable:
# If this is a subsequent target and it's linkable, don't look any
# further for linkable dependencies, as they'll already be linked into
@@ -1966,25 +2017,25 @@ def MergeLists(to, fro, to_file, fro_file, is_paths=False, append=True):
hashable_to_set = set(x for x in to if is_hashable(x))
for item in fro:
singleton = False
if isinstance(item, str) or isinstance(item, int):
if type(item) in (str, int):
# The cheap and easy case.
if is_paths:
to_item = MakePathRelative(to_file, fro_file, item)
else:
to_item = item
if not isinstance(item, str) or not item.startswith('-'):
if not (type(item) is str and item.startswith('-')):
# Any string that doesn't begin with a "-" is a singleton - it can
# only appear once in a list, to be enforced by the list merge append
# or prepend.
singleton = True
elif isinstance(item, dict):
elif type(item) is dict:
# Make a copy of the dictionary, continuing to look for paths to fix.
# The other intelligent aspects of merge processing won't apply because
# item is being merged into an empty dict.
to_item = {}
MergeDicts(to_item, item, to_file, fro_file)
elif isinstance(item, list):
elif type(item) is list:
# Recurse, making a copy of the list. If the list contains any
# descendant dicts, path fixing will occur. Note that here, custom
# values for is_paths and append are dropped; those are only to be
@@ -2030,10 +2081,10 @@ def MergeDicts(to, fro, to_file, fro_file):
# modified.
if k in to:
bad_merge = False
if isinstance(v, str) or isinstance(v, int):
if not (isinstance(to[k], str) or isinstance(to[k], int)):
if type(v) in (str, int):
if type(to[k]) not in (str, int):
bad_merge = True
elif v.__class__ != to[k].__class__:
elif type(v) is not type(to[k]):
bad_merge = True
if bad_merge:
@@ -2041,19 +2092,19 @@ def MergeDicts(to, fro, to_file, fro_file):
'Attempt to merge dict value of type ' + v.__class__.__name__ + \
' into incompatible type ' + to[k].__class__.__name__ + \
' for key ' + k
if isinstance(v, str) or isinstance(v, int):
if type(v) in (str, int):
# Overwrite the existing value, if any. Cheap and easy.
is_path = IsPathSection(k)
if is_path:
to[k] = MakePathRelative(to_file, fro_file, v)
else:
to[k] = v
elif isinstance(v, dict):
elif type(v) is dict:
# Recurse, guaranteeing copies will be made of objects that require it.
if not k in to:
to[k] = {}
MergeDicts(to[k], v, to_file, fro_file)
elif isinstance(v, list):
elif type(v) is list:
# Lists in dicts can be merged with different policies, depending on
# how the key in the "from" dict (k, the from-key) is written.
#
@@ -2096,7 +2147,7 @@ def MergeDicts(to, fro, to_file, fro_file):
# If the key ends in "?", the list will only be merged if it doesn't
# already exist.
continue
if not isinstance(to[list_base], list):
elif type(to[list_base]) is not list:
# This may not have been checked above if merging in a list with an
# extension character.
raise TypeError, \
@@ -2157,43 +2208,39 @@ def SetUpConfigurations(target, target_dict):
if not 'configurations' in target_dict:
target_dict['configurations'] = {'Default': {}}
if not 'default_configuration' in target_dict:
concrete = [i for i in target_dict['configurations'].iterkeys()
if not target_dict['configurations'][i].get('abstract')]
concrete = [i for (i, config) in target_dict['configurations'].iteritems()
if not config.get('abstract')]
target_dict['default_configuration'] = sorted(concrete)[0]
for configuration in target_dict['configurations'].keys():
old_configuration_dict = target_dict['configurations'][configuration]
merged_configurations = {}
configs = target_dict['configurations']
for (configuration, old_configuration_dict) in configs.iteritems():
# Skip abstract configurations (saves work only).
if old_configuration_dict.get('abstract'):
continue
# Configurations inherit (most) settings from the enclosing target scope.
# Get the inheritance relationship right by making a copy of the target
# dict.
new_configuration_dict = copy.deepcopy(target_dict)
# Take out the bits that don't belong in a "configurations" section.
# Since configuration setup is done before conditional, exclude, and rules
# processing, be careful with handling of the suffix characters used in
# those phases.
delete_keys = []
for key in new_configuration_dict:
new_configuration_dict = {}
for (key, target_val) in target_dict.iteritems():
key_ext = key[-1:]
if key_ext in key_suffixes:
key_base = key[:-1]
else:
key_base = key
if key_base in non_configuration_keys:
delete_keys.append(key)
for key in delete_keys:
del new_configuration_dict[key]
if not key_base in non_configuration_keys:
new_configuration_dict[key] = gyp.simple_copy.deepcopy(target_val)
# Merge in configuration (with all its parents first).
MergeConfigWithInheritance(new_configuration_dict, build_file,
target_dict, configuration, [])
# Put the new result back into the target dict as a configuration.
target_dict['configurations'][configuration] = new_configuration_dict
merged_configurations[configuration] = new_configuration_dict
# Put the new configurations back into the target dict as a configuration.
for configuration in merged_configurations.keys():
target_dict['configurations'][configuration] = (
merged_configurations[configuration])
# Now drop all the abstract ones.
for configuration in target_dict['configurations'].keys():
@@ -2264,7 +2311,7 @@ def ProcessListFiltersInDict(name, the_dict):
if operation != '!' and operation != '/':
continue
if not isinstance(value, list):
if type(value) is not list:
raise ValueError, name + ' key ' + key + ' must be list, not ' + \
value.__class__.__name__
@@ -2276,7 +2323,7 @@ def ProcessListFiltersInDict(name, the_dict):
del_lists.append(key)
continue
if not isinstance(the_dict[list_key], list):
if type(the_dict[list_key]) is not list:
value = the_dict[list_key]
raise ValueError, name + ' key ' + list_key + \
' must be list, not ' + \
@@ -2378,17 +2425,17 @@ def ProcessListFiltersInDict(name, the_dict):
# Now recurse into subdicts and lists that may contain dicts.
for key, value in the_dict.iteritems():
if isinstance(value, dict):
if type(value) is dict:
ProcessListFiltersInDict(key, value)
elif isinstance(value, list):
elif type(value) is list:
ProcessListFiltersInList(key, value)
def ProcessListFiltersInList(name, the_list):
for item in the_list:
if isinstance(item, dict):
if type(item) is dict:
ProcessListFiltersInDict(name, item)
elif isinstance(item, list):
elif type(item) is list:
ProcessListFiltersInList(name, item)
@@ -2506,7 +2553,7 @@ def ValidateRunAsInTarget(target, target_dict, build_file):
run_as = target_dict.get('run_as')
if not run_as:
return
if not isinstance(run_as, dict):
if type(run_as) is not dict:
raise GypError("The 'run_as' in target %s from file %s should be a "
"dictionary." %
(target_name, build_file))
@@ -2515,17 +2562,17 @@ def ValidateRunAsInTarget(target, target_dict, build_file):
raise GypError("The 'run_as' in target %s from file %s must have an "
"'action' section." %
(target_name, build_file))
if not isinstance(action, list):
if type(action) is not list:
raise GypError("The 'action' for 'run_as' in target %s from file %s "
"must be a list." %
(target_name, build_file))
working_directory = run_as.get('working_directory')
if working_directory and not isinstance(working_directory, str):
if working_directory and type(working_directory) is not str:
raise GypError("The 'working_directory' for 'run_as' in target %s "
"in file %s should be a string." %
(target_name, build_file))
environment = run_as.get('environment')
if environment and not isinstance(environment, dict):
if environment and type(environment) is not dict:
raise GypError("The 'environment' for 'run_as' in target %s "
"in file %s should be a dictionary." %
(target_name, build_file))
@@ -2555,17 +2602,17 @@ def TurnIntIntoStrInDict(the_dict):
# Use items instead of iteritems because there's no need to try to look at
# reinserted keys and their associated values.
for k, v in the_dict.items():
if isinstance(v, int):
if type(v) is int:
v = str(v)
the_dict[k] = v
elif isinstance(v, dict):
elif type(v) is dict:
TurnIntIntoStrInDict(v)
elif isinstance(v, list):
elif type(v) is list:
TurnIntIntoStrInList(v)
if isinstance(k, int):
the_dict[str(k)] = v
if type(k) is int:
del the_dict[k]
the_dict[str(k)] = v
def TurnIntIntoStrInList(the_list):
@@ -2573,11 +2620,11 @@ def TurnIntIntoStrInList(the_list):
"""
for index in xrange(0, len(the_list)):
item = the_list[index]
if isinstance(item, int):
if type(item) is int:
the_list[index] = str(item)
elif isinstance(item, dict):
elif type(item) is dict:
TurnIntIntoStrInDict(item)
elif isinstance(item, list):
elif type(item) is list:
TurnIntIntoStrInList(item)
@@ -2647,8 +2694,8 @@ def SetGeneratorGlobals(generator_input_info):
# Set up path_sections and non_configuration_keys with the default data plus
# the generator-specific data.
global path_sections
path_sections = base_path_sections[:]
path_sections.extend(generator_input_info['path_sections'])
path_sections = set(base_path_sections)
path_sections.update(generator_input_info['path_sections'])
global non_configuration_keys
non_configuration_keys = base_non_configuration_keys[:]
@@ -2707,6 +2754,10 @@ def Load(build_files, variables, includes, depth, generator_input_info, check,
# Expand dependencies specified as build_file:*.
ExpandWildcardDependencies(targets, data)
# Remove all dependencies marked as 'link_dependency' from the targets of
# type 'none'.
RemoveLinkDependenciesFromNoneTargets(targets)
# Apply exclude (!) and regex (/) list filters only for dependency_sections.
for target_name, target_dict in targets.iteritems():
tmp_dict = {}

View File

@@ -219,10 +219,14 @@ class MacTool(object):
"""Calls libtool and filters out '/path/to/libtool: file: foo.o has no
symbols'."""
libtool_re = re.compile(r'^.*libtool: file: .* has no symbols$')
libtool_re5 = re.compile(
r'^.*libtool: warning for library: ' +
r'.* the table of contents is empty ' +
r'\(no object file members in the library define global symbols\)$')
libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE)
_, err = libtoolout.communicate()
for line in err.splitlines():
if not libtool_re.match(line):
if not libtool_re.match(line) and not libtool_re5.match(line):
print >>sys.stderr, line
return libtoolout.returncode

View File

@@ -345,6 +345,15 @@ class MsvsSettings(object):
else:
return None
def GetAsmflags(self, config):
"""Returns the flags that need to be added to ml invocations."""
config = self._TargetConfig(config)
asmflags = []
safeseh = self._Setting(('MASM', 'UseSafeExceptionHandlers'), config)
if safeseh == 'true':
asmflags.append('/safeseh')
return asmflags
def GetCflags(self, config):
"""Returns the flags that need to be added to .c and .cc compilations."""
config = self._TargetConfig(config)
@@ -379,6 +388,8 @@ class MsvsSettings(object):
map={'false': '-', 'true': ''}, prefix='/Zc:wchar_t')
cl('EnablePREfast', map={'true': '/analyze'})
cl('AdditionalOptions', prefix='')
cl('EnableEnhancedInstructionSet',
map={'1': 'SSE', '2': 'SSE2', '3': 'AVX', '4': 'IA32'}, prefix='/arch:')
cflags.extend(['/FI' + f for f in self._Setting(
('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])])
if self.vs_version.short_name in ('2013', '2013e'):
@@ -388,12 +399,6 @@ class MsvsSettings(object):
cflags = filter(lambda x: not x.startswith('/MP'), cflags)
return cflags
def GetPrecompiledHeader(self, config, gyp_to_build_path):
"""Returns an object that handles the generation of precompiled header
build steps."""
config = self._TargetConfig(config)
return _PchHelper(self, config, gyp_to_build_path)
def _GetPchFlags(self, config, extension):
"""Get the flags to be added to the cflags for precompiled header support.
"""
@@ -532,6 +537,7 @@ class MsvsSettings(object):
ld('Profile', map={'true': '/PROFILE'})
ld('LargeAddressAware',
map={'1': ':NO', '2': ''}, prefix='/LARGEADDRESSAWARE')
ld('ImageHasSafeExceptionHandlers', map={'true': '/SAFESEH'})
# TODO(scottmg): This should sort of be somewhere else (not really a flag).
ld('AdditionalDependencies', prefix='')
@@ -787,7 +793,7 @@ class PrecompiledHeader(object):
def GetObjDependencies(self, sources, objs, arch):
"""Given a list of sources files and the corresponding object files,
returns a list of the pch files that should be depended upon. The
additional wrapping in the return value is for interface compatability
additional wrapping in the return value is for interface compatibility
with make.py on Mac, and xcode_emulation.py."""
assert arch is None
if not self._PchHeader():

46
deps/gyp/pylib/gyp/simple_copy.py vendored Normal file
View File

@@ -0,0 +1,46 @@
# Copyright 2014 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A clone of the default copy.deepcopy that doesn't handle cyclic
structures or complex types except for dicts and lists. This is
because gyp copies so large structure that small copy overhead ends up
taking seconds in a project the size of Chromium."""
class Error(Exception):
pass
__all__ = ["Error", "deepcopy"]
def deepcopy(x):
"""Deep copy operation on gyp objects such as strings, ints, dicts
and lists. More than twice as fast as copy.deepcopy but much less
generic."""
try:
return _deepcopy_dispatch[type(x)](x)
except KeyError:
raise Error('Unsupported type %s for deepcopy. Use copy.deepcopy ' +
'or expand simple_copy support.' % type(x))
_deepcopy_dispatch = d = {}
def _deepcopy_atomic(x):
return x
for x in (type(None), int, long, float,
bool, str, unicode, type):
d[x] = _deepcopy_atomic
def _deepcopy_list(x):
return [deepcopy(a) for a in x]
d[list] = _deepcopy_list
def _deepcopy_dict(x):
y = {}
for key, value in x.iteritems():
y[deepcopy(key)] = deepcopy(value)
return y
d[dict] = _deepcopy_dict
del d

View File

@@ -13,6 +13,7 @@ import os
import re
import shutil
import subprocess
import stat
import string
import sys
@@ -89,9 +90,19 @@ class WinTool(object):
"""Emulation of rm -rf out && cp -af in out."""
if os.path.exists(dest):
if os.path.isdir(dest):
shutil.rmtree(dest)
def _on_error(fn, path, excinfo):
# The operation failed, possibly because the file is set to
# read-only. If that's why, make it writable and try the op again.
if not os.access(path, os.W_OK):
os.chmod(path, stat.S_IWRITE)
fn(path)
shutil.rmtree(dest, onerror=_on_error)
else:
if not os.access(dest, os.W_OK):
# Attempt to make the file writable before deleting it.
os.chmod(dest, stat.S_IWRITE)
os.unlink(dest)
if os.path.isdir(source):
shutil.copytree(source, dest)
else:
@@ -237,10 +248,11 @@ class WinTool(object):
# Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl
# objidl.idl
lines = out.splitlines()
prefix = 'Processing '
processing = set(os.path.basename(x) for x in lines if x.startswith(prefix))
prefixes = ('Processing ', '64 bit Processing ')
processing = set(os.path.basename(x)
for x in lines if x.startswith(prefixes))
for line in lines:
if not line.startswith(prefix) and line not in processing:
if not line.startswith(prefixes) and line not in processing:
print line
return popen.returncode
@@ -288,5 +300,16 @@ class WinTool(object):
dir = dir[0] if dir else None
return subprocess.call(args, shell=True, env=env, cwd=dir)
def ExecClCompile(self, project_dir, selected_files):
"""Executed by msvs-ninja projects when the 'ClCompile' target is used to
build selected C/C++ files."""
project_dir = os.path.relpath(project_dir, BASE_DIR)
selected_files = selected_files.split(';')
ninja_targets = [os.path.join(project_dir, filename) + '^^'
for filename in selected_files]
cmd = ['ninja.exe']
cmd.extend(ninja_targets)
return subprocess.call(cmd, shell=True, cwd=BASE_DIR)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@@ -18,6 +18,129 @@ import sys
import tempfile
from gyp.common import GypError
# Populated lazily by XcodeVersion, for efficiency, and to fix an issue when
# "xcodebuild" is called too quickly (it has been found to return incorrect
# version number).
XCODE_VERSION_CACHE = None
# Populated lazily by GetXcodeArchsDefault, to an |XcodeArchsDefault| instance
# corresponding to the installed version of Xcode.
XCODE_ARCHS_DEFAULT_CACHE = None
def XcodeArchsVariableMapping(archs, archs_including_64_bit=None):
"""Constructs a dictionary with expansion for $(ARCHS_STANDARD) variable,
and optionally for $(ARCHS_STANDARD_INCLUDING_64_BIT)."""
mapping = {'$(ARCHS_STANDARD)': archs}
if archs_including_64_bit:
mapping['$(ARCHS_STANDARD_INCLUDING_64_BIT)'] = archs_including_64_bit
return mapping
class XcodeArchsDefault(object):
"""A class to resolve ARCHS variable from xcode_settings, resolving Xcode
macros and implementing filtering by VALID_ARCHS. The expansion of macros
depends on the SDKROOT used ("macosx", "iphoneos", "iphonesimulator") and
on the version of Xcode.
"""
# Match variable like $(ARCHS_STANDARD).
variable_pattern = re.compile(r'\$\([a-zA-Z_][a-zA-Z0-9_]*\)$')
def __init__(self, default, mac, iphonesimulator, iphoneos):
self._default = (default,)
self._archs = {'mac': mac, 'ios': iphoneos, 'iossim': iphonesimulator}
def _VariableMapping(self, sdkroot):
"""Returns the dictionary of variable mapping depending on the SDKROOT."""
sdkroot = sdkroot.lower()
if 'iphoneos' in sdkroot:
return self._archs['ios']
elif 'iphonesimulator' in sdkroot:
return self._archs['iossim']
else:
return self._archs['mac']
def _ExpandArchs(self, archs, sdkroot):
"""Expands variables references in ARCHS, and remove duplicates."""
variable_mapping = self._VariableMapping(sdkroot)
expanded_archs = []
for arch in archs:
if self.variable_pattern.match(arch):
variable = arch
try:
variable_expansion = variable_mapping[variable]
for arch in variable_expansion:
if arch not in expanded_archs:
expanded_archs.append(arch)
except KeyError as e:
print 'Warning: Ignoring unsupported variable "%s".' % variable
elif arch not in expanded_archs:
expanded_archs.append(arch)
return expanded_archs
def ActiveArchs(self, archs, valid_archs, sdkroot):
"""Expands variables references in ARCHS, and filter by VALID_ARCHS if it
is defined (if not set, Xcode accept any value in ARCHS, otherwise, only
values present in VALID_ARCHS are kept)."""
expanded_archs = self._ExpandArchs(archs or self._default, sdkroot or '')
if valid_archs:
filtered_archs = []
for arch in expanded_archs:
if arch in valid_archs:
filtered_archs.append(arch)
expanded_archs = filtered_archs
return expanded_archs
def GetXcodeArchsDefault():
"""Returns the |XcodeArchsDefault| object to use to expand ARCHS for the
installed version of Xcode. The default values used by Xcode for ARCHS
and the expansion of the variables depends on the version of Xcode used.
For all version anterior to Xcode 5.0 or posterior to Xcode 5.1 included
uses $(ARCHS_STANDARD) if ARCHS is unset, while Xcode 5.0 to 5.0.2 uses
$(ARCHS_STANDARD_INCLUDING_64_BIT). This variable was added to Xcode 5.0
and deprecated with Xcode 5.1.
For "macosx" SDKROOT, all version starting with Xcode 5.0 includes 64-bit
architecture as part of $(ARCHS_STANDARD) and default to only building it.
For "iphoneos" and "iphonesimulator" SDKROOT, 64-bit architectures are part
of $(ARCHS_STANDARD_INCLUDING_64_BIT) from Xcode 5.0. From Xcode 5.1, they
are also part of $(ARCHS_STANDARD).
All thoses rules are coded in the construction of the |XcodeArchsDefault|
object to use depending on the version of Xcode detected. The object is
for performance reason."""
global XCODE_ARCHS_DEFAULT_CACHE
if XCODE_ARCHS_DEFAULT_CACHE:
return XCODE_ARCHS_DEFAULT_CACHE
xcode_version, _ = XcodeVersion()
if xcode_version < '0500':
XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
'$(ARCHS_STANDARD)',
XcodeArchsVariableMapping(['i386']),
XcodeArchsVariableMapping(['i386']),
XcodeArchsVariableMapping(['armv7']))
elif xcode_version < '0510':
XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
'$(ARCHS_STANDARD_INCLUDING_64_BIT)',
XcodeArchsVariableMapping(['x86_64'], ['x86_64']),
XcodeArchsVariableMapping(['i386'], ['i386', 'x86_64']),
XcodeArchsVariableMapping(
['armv7', 'armv7s'],
['armv7', 'armv7s', 'arm64']))
else:
XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
'$(ARCHS_STANDARD)',
XcodeArchsVariableMapping(['x86_64'], ['x86_64']),
XcodeArchsVariableMapping(['i386', 'x86_64'], ['i386', 'x86_64']),
XcodeArchsVariableMapping(
['armv7', 'armv7s', 'arm64'],
['armv7', 'armv7s', 'arm64']))
return XCODE_ARCHS_DEFAULT_CACHE
class XcodeSettings(object):
"""A class that understands the gyp 'xcode_settings' object."""
@@ -34,10 +157,6 @@ class XcodeSettings(object):
# cached at class-level for efficiency.
_codesigning_key_cache = {}
# Populated lazily by _XcodeVersion. Shared by all XcodeSettings, so cached
# at class-level for efficiency.
_xcode_version_cache = ()
def __init__(self, spec):
self.spec = spec
@@ -267,17 +386,12 @@ class XcodeSettings(object):
def GetActiveArchs(self, configname):
"""Returns the architectures this target should be built for."""
# TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set
# CURRENT_ARCH / NATIVE_ARCH env vars?
return self.xcode_settings[configname].get('ARCHS', [self._DefaultArch()])
def _GetStdout(self, cmdlist):
job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE)
out = job.communicate()[0]
if job.returncode != 0:
sys.stderr.write(out + '\n')
raise GypError('Error %d running %s' % (job.returncode, cmdlist[0]))
return out.rstrip('\n')
config_settings = self.xcode_settings[configname]
xcode_archs_default = GetXcodeArchsDefault()
return xcode_archs_default.ActiveArchs(
config_settings.get('ARCHS'),
config_settings.get('VALID_ARCHS'),
config_settings.get('SDKROOT'))
def _GetSdkVersionInfoItem(self, sdk, infoitem):
# xcodebuild requires Xcode and can't run on Command Line Tools-only
@@ -285,7 +399,7 @@ class XcodeSettings(object):
# Since the CLT has no SDK paths anyway, returning None is the
# most sensible route and should still do the right thing.
try:
return self._GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem])
return GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem])
except:
pass
@@ -396,7 +510,8 @@ class XcodeSettings(object):
if arch is not None:
archs = [arch]
else:
archs = self._Settings().get('ARCHS', [self._DefaultArch()])
assert self.configname
archs = self.GetActiveArchs(self.configname)
if len(archs) != 1:
# TODO: Supporting fat binaries will be annoying.
self._WarnUnimplemented('ARCHS')
@@ -653,7 +768,8 @@ class XcodeSettings(object):
if arch is not None:
archs = [arch]
else:
archs = self._Settings().get('ARCHS', [self._DefaultArch()])
assert self.configname
archs = self.GetActiveArchs(self.configname)
if len(archs) != 1:
# TODO: Supporting fat binaries will be annoying.
self._WarnUnimplemented('ARCHS')
@@ -678,6 +794,8 @@ class XcodeSettings(object):
for directory in framework_dirs:
ldflags.append('-F' + directory.replace('$(SDKROOT)', sdk_root))
self._Appendf(ldflags, 'CLANG_CXX_LIBRARY', '-stdlib=%s')
self.configname = None
return ldflags
@@ -874,65 +992,7 @@ class XcodeSettings(object):
return libraries
def _BuildMachineOSBuild(self):
return self._GetStdout(['sw_vers', '-buildVersion'])
# This method ported from the logic in Homebrew's CLT version check
def _CLTVersion(self):
# pkgutil output looks like
# package-id: com.apple.pkg.CLTools_Executables
# version: 5.0.1.0.1.1382131676
# volume: /
# location: /
# install-time: 1382544035
# groups: com.apple.FindSystemFiles.pkg-group com.apple.DevToolsBoth.pkg-group com.apple.DevToolsNonRelocatableShared.pkg-group
STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo"
FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI"
MAVERICKS_PKG_ID = "com.apple.pkg.CLTools_Executables"
regex = re.compile('version: (?P<version>.+)')
for key in [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID]:
try:
output = self._GetStdout(['/usr/sbin/pkgutil', '--pkg-info', key])
return re.search(regex, output).groupdict()['version']
except:
continue
def _XcodeVersion(self):
# `xcodebuild -version` output looks like
# Xcode 4.6.3
# Build version 4H1503
# or like
# Xcode 3.2.6
# Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0
# BuildVersion: 10M2518
# Convert that to '0463', '4H1503'.
if len(XcodeSettings._xcode_version_cache) == 0:
try:
version_list = self._GetStdout(['xcodebuild', '-version']).splitlines()
# In some circumstances xcodebuild exits 0 but doesn't return
# the right results; for example, a user on 10.7 or 10.8 with
# a bogus path set via xcode-select
# In that case this may be a CLT-only install so fall back to
# checking that version.
if len(version_list) < 2:
raise GypError, "xcodebuild returned unexpected results"
except:
version = self._CLTVersion()
if version:
version = re.match('(\d\.\d\.?\d*)', version).groups()[0]
else:
raise GypError, "No Xcode or CLT version detected!"
# The CLT has no build information, so we return an empty string.
version_list = [version, '']
version = version_list[0]
build = version_list[-1]
# Be careful to convert "4.2" to "0420":
version = version.split()[-1].replace('.', '')
version = (version + '0' * (3 - len(version))).zfill(4)
if build:
build = build.split()[-1]
XcodeSettings._xcode_version_cache = (version, build)
return XcodeSettings._xcode_version_cache
return GetStdout(['sw_vers', '-buildVersion'])
def _XcodeIOSDeviceFamily(self, configname):
family = self.xcode_settings[configname].get('TARGETED_DEVICE_FAMILY', '1')
@@ -944,7 +1004,7 @@ class XcodeSettings(object):
cache = {}
cache['BuildMachineOSBuild'] = self._BuildMachineOSBuild()
xcode, xcode_build = self._XcodeVersion()
xcode, xcode_build = XcodeVersion()
cache['DTXcode'] = xcode
cache['DTXcodeBuild'] = xcode_build
@@ -982,14 +1042,15 @@ class XcodeSettings(object):
project, then the environment variable was empty. Starting with this
version, Xcode uses the name of the newest SDK installed.
"""
if self._XcodeVersion() < '0500':
xcode_version, xcode_build = XcodeVersion()
if xcode_version < '0500':
return ''
default_sdk_path = self._XcodeSdkPath('')
default_sdk_root = XcodeSettings._sdk_root_cache.get(default_sdk_path)
if default_sdk_root:
return default_sdk_root
try:
all_sdks = self._GetStdout(['xcodebuild', '-showsdks'])
all_sdks = GetStdout(['xcodebuild', '-showsdks'])
except:
# If xcodebuild fails, there will be no valid SDKs
return ''
@@ -1002,28 +1063,6 @@ class XcodeSettings(object):
return sdk_root
return ''
def _DefaultArch(self):
# For Mac projects, Xcode changed the default value used when ARCHS is not
# set from "i386" to "x86_64".
#
# For iOS projects, if ARCHS is unset, it defaults to "armv7 armv7s" when
# building for a device, and the simulator binaries are always build for
# "i386".
#
# For new projects, ARCHS is set to $(ARCHS_STANDARD_INCLUDING_64_BIT),
# which correspond to "armv7 armv7s arm64", and when building the simulator
# the architecture is either "i386" or "x86_64" depending on the simulated
# device (respectively 32-bit or 64-bit device).
#
# Since the value returned by this function is only used when ARCHS is not
# set, then on iOS we return "i386", as the default xcode project generator
# does not set ARCHS if it is not set in the .gyp file.
if self.isIOS:
return 'i386'
version, build = self._XcodeVersion()
if version >= '0500':
return 'x86_64'
return 'i386'
class MacPrefixHeader(object):
"""A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature.
@@ -1131,6 +1170,81 @@ class MacPrefixHeader(object):
]
def XcodeVersion():
"""Returns a tuple of version and build version of installed Xcode."""
# `xcodebuild -version` output looks like
# Xcode 4.6.3
# Build version 4H1503
# or like
# Xcode 3.2.6
# Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0
# BuildVersion: 10M2518
# Convert that to '0463', '4H1503'.
global XCODE_VERSION_CACHE
if XCODE_VERSION_CACHE:
return XCODE_VERSION_CACHE
try:
version_list = GetStdout(['xcodebuild', '-version']).splitlines()
# In some circumstances xcodebuild exits 0 but doesn't return
# the right results; for example, a user on 10.7 or 10.8 with
# a bogus path set via xcode-select
# In that case this may be a CLT-only install so fall back to
# checking that version.
if len(version_list) < 2:
raise GypError, "xcodebuild returned unexpected results"
except:
version = CLTVersion()
if version:
version = re.match('(\d\.\d\.?\d*)', version).groups()[0]
else:
raise GypError, "No Xcode or CLT version detected!"
# The CLT has no build information, so we return an empty string.
version_list = [version, '']
version = version_list[0]
build = version_list[-1]
# Be careful to convert "4.2" to "0420":
version = version.split()[-1].replace('.', '')
version = (version + '0' * (3 - len(version))).zfill(4)
if build:
build = build.split()[-1]
XCODE_VERSION_CACHE = (version, build)
return XCODE_VERSION_CACHE
# This function ported from the logic in Homebrew's CLT version check
def CLTVersion():
"""Returns the version of command-line tools from pkgutil."""
# pkgutil output looks like
# package-id: com.apple.pkg.CLTools_Executables
# version: 5.0.1.0.1.1382131676
# volume: /
# location: /
# install-time: 1382544035
# groups: com.apple.FindSystemFiles.pkg-group com.apple.DevToolsBoth.pkg-group com.apple.DevToolsNonRelocatableShared.pkg-group
STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo"
FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI"
MAVERICKS_PKG_ID = "com.apple.pkg.CLTools_Executables"
regex = re.compile('version: (?P<version>.+)')
for key in [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID]:
try:
output = GetStdout(['/usr/sbin/pkgutil', '--pkg-info', key])
return re.search(regex, output).groupdict()['version']
except:
continue
def GetStdout(cmdlist):
"""Returns the content of standard output returned by invoking |cmdlist|.
Raises |GypError| if the command return with a non-zero return code."""
job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE)
out = job.communicate()[0]
if job.returncode != 0:
sys.stderr.write(out + '\n')
raise GypError('Error %d running %s' % (job.returncode, cmdlist[0]))
return out.rstrip('\n')
def MergeGlobalXcodeSettingsToSpec(global_dict, spec):
"""Merges the global xcode_settings dictionary into each configuration of the
target represented by spec. For keys that are both in the global and the local
@@ -1310,6 +1424,11 @@ def _GetXcodeEnv(xcode_settings, built_products_dir, srcroot, configuration,
install_name_base = xcode_settings.GetInstallNameBase()
if install_name_base:
env['DYLIB_INSTALL_NAME_BASE'] = install_name_base
if XcodeVersion() >= '0500' and not env.get('SDKROOT'):
sdk_root = xcode_settings._SdkRoot(configuration)
if not sdk_root:
sdk_root = xcode_settings._XcodeSdkPath('')
env['SDKROOT'] = sdk_root
if not additional_settings:
additional_settings = {}
@@ -1420,16 +1539,16 @@ def _HasIOSTarget(targets):
def _AddIOSDeviceConfigurations(targets):
"""Clone all targets and append -iphoneos to the name. Configure these targets
to build for iOS devices."""
for target_dict in targets.values():
for config_name in target_dict['configurations'].keys():
config = target_dict['configurations'][config_name]
new_config_name = config_name + '-iphoneos'
new_config_dict = copy.deepcopy(config)
if target_dict['toolset'] == 'target':
new_config_dict['xcode_settings']['ARCHS'] = ['armv7']
new_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos'
target_dict['configurations'][new_config_name] = new_config_dict
to build for iOS devices and use correct architectures for those builds."""
for target_dict in targets.itervalues():
toolset = target_dict['toolset']
configs = target_dict['configurations']
for config_name, config_dict in dict(configs).iteritems():
iphoneos_config_dict = copy.deepcopy(config_dict)
configs[config_name + '-iphoneos'] = iphoneos_config_dict
configs[config_name + '-iphonesimulator'] = config_dict
if toolset == 'target':
iphoneos_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos'
return targets
def CloneConfigurationForDeviceAndEmulator(target_dicts):

257
deps/gyp/pylib/gyp/xcode_ninja.py vendored Normal file
View File

@@ -0,0 +1,257 @@
# Copyright (c) 2014 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Xcode-ninja wrapper project file generator.
This updates the data structures passed to the Xcode gyp generator to build
with ninja instead. The Xcode project itself is transformed into a list of
executable targets, each with a build step to build with ninja, and a target
with every source and resource file. This appears to sidestep some of the
major performance headaches experienced using complex projects and large number
of targets within Xcode.
"""
import errno
import gyp.generator.ninja
import os
import re
import xml.sax.saxutils
def _WriteWorkspace(main_gyp, sources_gyp):
""" Create a workspace to wrap main and sources gyp paths. """
(build_file_root, build_file_ext) = os.path.splitext(main_gyp)
workspace_path = build_file_root + '.xcworkspace'
try:
os.makedirs(workspace_path)
except OSError, e:
if e.errno != errno.EEXIST:
raise
output_string = '<?xml version="1.0" encoding="UTF-8"?>\n' + \
'<Workspace version = "1.0">\n'
for gyp_name in [main_gyp, sources_gyp]:
name = os.path.splitext(os.path.basename(gyp_name))[0] + '.xcodeproj'
name = xml.sax.saxutils.quoteattr("group:" + name)
output_string += ' <FileRef location = %s></FileRef>\n' % name
output_string += '</Workspace>\n'
workspace_file = os.path.join(workspace_path, "contents.xcworkspacedata")
try:
with open(workspace_file, 'r') as input_file:
input_string = input_file.read()
if input_string == output_string:
return
except IOError:
# Ignore errors if the file doesn't exist.
pass
with open(workspace_file, 'w') as output_file:
output_file.write(output_string)
def _TargetFromSpec(old_spec, params):
""" Create fake target for xcode-ninja wrapper. """
# Determine ninja top level build dir (e.g. /path/to/out).
ninja_toplevel = None
jobs = 0
if params:
options = params['options']
ninja_toplevel = \
os.path.join(options.toplevel_dir,
gyp.generator.ninja.ComputeOutputDir(params))
jobs = params.get('generator_flags', {}).get('xcode_ninja_jobs', 0)
target_name = old_spec.get('target_name')
product_name = old_spec.get('product_name', target_name)
ninja_target = {}
ninja_target['target_name'] = target_name
ninja_target['product_name'] = product_name
ninja_target['toolset'] = old_spec.get('toolset')
ninja_target['default_configuration'] = old_spec.get('default_configuration')
ninja_target['configurations'] = {}
# Tell Xcode to look in |ninja_toplevel| for build products.
new_xcode_settings = {}
if ninja_toplevel:
new_xcode_settings['CONFIGURATION_BUILD_DIR'] = \
"%s/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)" % ninja_toplevel
if 'configurations' in old_spec:
for config in old_spec['configurations'].iterkeys():
old_xcode_settings = old_spec['configurations'][config]['xcode_settings']
if 'IPHONEOS_DEPLOYMENT_TARGET' in old_xcode_settings:
new_xcode_settings['CODE_SIGNING_REQUIRED'] = "NO"
new_xcode_settings['IPHONEOS_DEPLOYMENT_TARGET'] = \
old_xcode_settings['IPHONEOS_DEPLOYMENT_TARGET']
ninja_target['configurations'][config] = {}
ninja_target['configurations'][config]['xcode_settings'] = \
new_xcode_settings
ninja_target['mac_bundle'] = old_spec.get('mac_bundle', 0)
ninja_target['type'] = old_spec['type']
if ninja_toplevel:
ninja_target['actions'] = [
{
'action_name': 'Compile and copy %s via ninja' % target_name,
'inputs': [],
'outputs': [],
'action': [
'env',
'PATH=%s' % os.environ['PATH'],
'ninja',
'-C',
new_xcode_settings['CONFIGURATION_BUILD_DIR'],
target_name,
],
'message': 'Compile and copy %s via ninja' % target_name,
},
]
if jobs > 0:
ninja_target['actions'][0]['action'].extend(('-j', jobs))
return ninja_target
def IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
"""Limit targets for Xcode wrapper.
Xcode sometimes performs poorly with too many targets, so only include
proper executable targets, with filters to customize.
Arguments:
target_extras: Regular expression to always add, matching any target.
executable_target_pattern: Regular expression limiting executable targets.
spec: Specifications for target.
"""
target_name = spec.get('target_name')
# Always include targets matching target_extras.
if target_extras is not None and re.search(target_extras, target_name):
return True
# Otherwise just show executable targets.
if spec.get('type', '') == 'executable' and \
spec.get('product_extension', '') != 'bundle':
# If there is a filter and the target does not match, exclude the target.
if executable_target_pattern is not None:
if not re.search(executable_target_pattern, target_name):
return False
return True
return False
def CreateWrapper(target_list, target_dicts, data, params):
"""Initialize targets for the ninja wrapper.
This sets up the necessary variables in the targets to generate Xcode projects
that use ninja as an external builder.
Arguments:
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
data: Dict of flattened build files keyed on gyp path.
params: Dict of global options for gyp.
"""
orig_gyp = params['build_files'][0]
for gyp_name, gyp_dict in data.iteritems():
if gyp_name == orig_gyp:
depth = gyp_dict['_DEPTH']
# Check for custom main gyp name, otherwise use the default CHROMIUM_GYP_FILE
# and prepend .ninja before the .gyp extension.
generator_flags = params.get('generator_flags', {})
main_gyp = generator_flags.get('xcode_ninja_main_gyp', None)
if main_gyp is None:
(build_file_root, build_file_ext) = os.path.splitext(orig_gyp)
main_gyp = build_file_root + ".ninja" + build_file_ext
# Create new |target_list|, |target_dicts| and |data| data structures.
new_target_list = []
new_target_dicts = {}
new_data = {}
# Set base keys needed for |data|.
new_data[main_gyp] = {}
new_data[main_gyp]['included_files'] = []
new_data[main_gyp]['targets'] = []
new_data[main_gyp]['xcode_settings'] = \
data[orig_gyp].get('xcode_settings', {})
# Normally the xcode-ninja generator includes only valid executable targets.
# If |xcode_ninja_executable_target_pattern| is set, that list is reduced to
# executable targets that match the pattern. (Default all)
executable_target_pattern = \
generator_flags.get('xcode_ninja_executable_target_pattern', None)
# For including other non-executable targets, add the matching target name
# to the |xcode_ninja_target_pattern| regular expression. (Default none)
target_extras = generator_flags.get('xcode_ninja_target_pattern', None)
for old_qualified_target in target_list:
spec = target_dicts[old_qualified_target]
if IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
# Add to new_target_list.
target_name = spec.get('target_name')
new_target_name = '%s:%s#target' % (main_gyp, target_name)
new_target_list.append(new_target_name)
# Add to new_target_dicts.
new_target_dicts[new_target_name] = _TargetFromSpec(spec, params)
# Add to new_data.
for old_target in data[old_qualified_target.split(':')[0]]['targets']:
if old_target['target_name'] == target_name:
new_data_target = {}
new_data_target['target_name'] = old_target['target_name']
new_data_target['toolset'] = old_target['toolset']
new_data[main_gyp]['targets'].append(new_data_target)
# Create sources target.
sources_target_name = 'sources_for_indexing'
sources_target = _TargetFromSpec(
{ 'target_name' : sources_target_name,
'toolset': 'target',
'default_configuration': 'Default',
'mac_bundle': '0',
'type': 'executable'
}, None)
# Tell Xcode to look everywhere for headers.
sources_target['configurations'] = {'Default': { 'include_dirs': [ depth ] } }
sources = []
for target, target_dict in target_dicts.iteritems():
base = os.path.dirname(target)
files = target_dict.get('sources', []) + \
target_dict.get('mac_bundle_resources', [])
# Remove files starting with $. These are mostly intermediate files for the
# build system.
files = [ file for file in files if not file.startswith('$')]
# Make sources relative to root build file.
relative_path = os.path.dirname(main_gyp)
sources += [ os.path.relpath(os.path.join(base, file), relative_path)
for file in files ]
sources_target['sources'] = sorted(set(sources))
# Put sources_to_index in it's own gyp.
sources_gyp = \
os.path.join(os.path.dirname(main_gyp), sources_target_name + ".gyp")
fully_qualified_target_name = \
'%s:%s#target' % (sources_gyp, sources_target_name)
# Add to new_target_list, new_target_dicts and new_data.
new_target_list.append(fully_qualified_target_name)
new_target_dicts[fully_qualified_target_name] = sources_target
new_data_target = {}
new_data_target['target_name'] = sources_target['target_name']
new_data_target['_DEPTH'] = depth
new_data_target['toolset'] = "target"
new_data[sources_gyp] = {}
new_data[sources_gyp]['targets'] = []
new_data[sources_gyp]['included_files'] = []
new_data[sources_gyp]['xcode_settings'] = \
data[orig_gyp].get('xcode_settings', {})
new_data[sources_gyp]['targets'].append(new_data_target)
# Write workspace to file.
_WriteWorkspace(main_gyp, sources_gyp)
return (new_target_list, new_target_dicts, new_data)

19
deps/gyp/setup.py vendored Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python
# Copyright (c) 2009 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from setuptools import setup
setup(
name='gyp',
version='0.1',
description='Generate Your Projects',
author='Chromium Authors',
author_email='chromium-dev@googlegroups.com',
url='http://code.google.com/p/gyp',
package_dir = {'': 'pylib'},
packages=['gyp', 'gyp.generator'],
entry_points = {'console_scripts': ['gyp=gyp:script_main'] }
)

View File

@@ -2,6 +2,7 @@
#define LLMR_GEOMETRY_GLYPH_ATLAS
#include <llmr/geometry/binpack.hpp>
#include <llmr/text/glyph_store.hpp>
#include <string>
#include <set>
@@ -30,7 +31,7 @@ public:
Rect<uint16_t> addGlyph(uint64_t tile_id, const std::string& face_name,
const VectorTileGlyph& glyph);
const SDFGlyph& glyph);
void removeGlyphs(uint64_t tile_id);
void bind();

View File

@@ -8,6 +8,7 @@
#include <llmr/map/transform.hpp>
#include <llmr/style/style.hpp>
#include <llmr/geometry/glyph_atlas.hpp>
#include <llmr/text/glyph_store.hpp>
#include <llmr/renderer/painter.hpp>
#include <llmr/util/noncopyable.hpp>
#include <llmr/util/texturepool.hpp>
@@ -98,6 +99,7 @@ public:
inline const TransformState &getState() const { return state; }
inline const Style &getStyle() const { return style; }
inline GlyphAtlas &getGlyphAtlas() { return glyphAtlas; }
inline GlyphStore &getGlyphStore() { return glyphStore; }
inline SpriteAtlas &getSpriteAtlas() { return spriteAtlas; }
inline uv_loop_t *getLoop() { return loop; }
inline time getAnimationTime() const { return animationTime; }
@@ -152,6 +154,7 @@ private:
Texturepool texturepool;
Style style;
GlyphAtlas glyphAtlas;
GlyphStore glyphStore;
SpriteAtlas spriteAtlas;
Painter painter;

View File

@@ -9,6 +9,7 @@
#include <forward_list>
#include <string>
#include <bitset>
#include <memory>
namespace llmr {

View File

@@ -4,11 +4,15 @@
#include <llmr/map/vector_tile_data.hpp>
#include <llmr/map/vector_tile.hpp>
#include <llmr/text/placement.hpp>
#include <llmr/text/glyph_store.hpp>
#include <llmr/text/glyph.hpp>
#include <llmr/util/utf.hpp>
namespace llmr {
class Style;
class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
class LayerDescription;
@@ -16,12 +20,13 @@ class Bucket;
class TileParser {
public:
TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, SpriteAtlas &spriteAtlas);
TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, GlyphStore &glyphStore, SpriteAtlas &spriteAtlas);
private:
bool obsolete() const;
void parseGlyphs();
void parseStyleLayers(const std::vector<LayerDescription>& layers);
void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
std::unique_ptr<Bucket> createBucket(const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
@@ -35,6 +40,7 @@ private:
VectorTileData& tile;
const Style& style;
GlyphAtlas& glyphAtlas;
GlyphStore &glyphStore;
SpriteAtlas &spriteAtlas;
Faces faces;
Placement placement;

View File

@@ -13,7 +13,7 @@
#include <llmr/geometry/icon_buffer.hpp>
#include <llmr/geometry/text_buffer.hpp>
#include <map>
#include <unordered_map>
namespace llmr {
@@ -42,7 +42,7 @@ protected:
// Holds the buckets of this tile.
// They contain the location offsets in the buffers stored above
std::map<std::string, std::unique_ptr<Bucket>> buckets;
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
};

View File

@@ -39,7 +39,7 @@
namespace llmr {
namespace gl {
// Debug group markers, useful for debuggin on iOS
#if GL_EXT_debug_marker
#if defined(__APPLE__) && defined(DEBUG) && defined(GL_EXT_debug_marker)
// static int indent = 0;
inline void start_group(const std::string &str) {
glPushGroupMarkerEXT(0, str.c_str());

View File

@@ -25,9 +25,11 @@ struct Response {
// Makes an HTTP request of a URL, preferrably on a background thread, and calls a function with the
// results in the original thread (which runs the libuv loop).
// If the loop pointer is NULL, the callback function will be called on an arbitrary thread.
// Returns a cancellable request.
std::shared_ptr<Request> request_http(const std::string &url,
std::function<void(Response *)> callback, uv_loop_t *loop);
std::function<void(Response *)> callback,
uv_loop_t *loop = nullptr);
// Cancels an HTTP request.
void cancel_request_http(const std::shared_ptr<Request> &req);

View File

@@ -2,6 +2,7 @@
#define LLMR_RENDERER_BUCKET
#include <string>
#include <memory>
#include <llmr/map/tile.hpp>
#include <llmr/util/noncopyable.hpp>
#include <llmr/renderer/prerendered_texture.hpp>

View File

@@ -26,7 +26,6 @@ private:
GLint previous_fbo = 0;
GLuint fbo = 0;
GLuint texture = 0;
bool mipmapped = false;
};
}

View File

@@ -7,6 +7,7 @@
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/map/vector_tile.hpp>
#include <llmr/text/types.hpp>
#include <llmr/text/glyph.hpp>
#include <memory>
#include <map>
#include <vector>
@@ -36,7 +37,7 @@ public:
PlacementRange placementRange, float zoom);
void addFeature(const VectorTileFeature &feature,
const IndexedFaces &faces,
const GlyphPositions &face,
const std::map<Value, Shaping> &shapings);
void drawGlyphs(TextShader &shader);

View File

@@ -16,6 +16,7 @@ namespace llmr {
typedef std::array<float, 4> Color;
struct PropertyTransition {
inline PropertyTransition(uint16_t duration, uint16_t delay = 0) : duration(duration), delay(delay) {}
uint16_t duration = 0;
uint16_t delay = 0;
};

View File

@@ -47,7 +47,7 @@ private:
Value parseValue(JSVal value);
FunctionProperty::fn parseFunctionType(JSVal type);
FunctionProperty parseFunction(JSVal value);
PropertyTransition parseTransition(JSVal value, std::string property_name);
boost::optional<PropertyTransition> parseTransition(JSVal value, std::string property_name);
private:
std::map<std::string, const rapidjson::Value *> constants;

View File

@@ -11,7 +11,7 @@ public:
Collision();
~Collision();
PlacementProperty place(const PlacedGlyphs &boxes,
PlacementProperty place(const GlyphBoxes &boxes,
const CollisionAnchor &anchor,
float minPlacementScale, float maxPlacementScale,
float padding, bool horizontal, bool alwaysVisible);

View File

@@ -9,6 +9,11 @@
namespace llmr {
typedef std::pair<uint16_t, uint16_t> GlyphRange;
// Note: this only works for the BMP
GlyphRange getGlyphRange(uint32_t glyph);
struct GlyphMetrics {
operator bool() const {
return width == 0 && height == 0 && advance == 0;

View File

@@ -0,0 +1,80 @@
#ifndef LLMR_TEXT_GLYPH_STORE
#define LLMR_TEXT_GLYPH_STORE
#include <llmr/text/glyph.hpp>
#include <llmr/util/pbf.hpp>
#include <cstdint>
#include <vector>
#include <future>
#include <map>
#include <set>
#include <unordered_map>
namespace llmr {
class SDFGlyph {
public:
uint32_t id = 0;
// A signed distance field of the glyph with a border of 3 pixels.
std::string bitmap;
// Glyph metrics
GlyphMetrics metrics;
};
class FontStack {
public:
void insert(uint32_t id, const SDFGlyph &glyph);
const std::map<uint32_t, GlyphMetrics> &getMetrics() const;
const std::map<uint32_t, SDFGlyph> &getSDFs() const;
const Shaping getShaping(const std::u32string &string) const;
private:
std::map<uint32_t, std::string> bitmaps;
std::map<uint32_t, GlyphMetrics> metrics;
std::map<uint32_t, SDFGlyph> sdfs;
mutable std::mutex mtx;
};
class GlyphPBF {
public:
GlyphPBF(const std::string &fontStack, GlyphRange glyphRange);
void parse(FontStack &stack);
std::shared_future<GlyphPBF &> getFuture();
private:
std::string data;
std::promise<GlyphPBF &> promise;
std::shared_future<GlyphPBF &> future;
std::mutex mtx;
};
// Manages Glyphrange PBF loading.
class GlyphStore {
public:
// Block until all specified GlyphRanges of the specified font stack are loaded.
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
FontStack &getFontStack(const std::string &fontStack);
private:
// Loads an individual glyph range from the font stack and adds it to rangeSets
std::shared_future<GlyphPBF &> loadGlyphRange(const std::string &fontStack, std::map<GlyphRange, std::unique_ptr<GlyphPBF>> &rangeSets, GlyphRange range);
FontStack &createFontStack(const std::string &fontStack);
private:
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
std::mutex mtx;
};
}
#endif

View File

@@ -18,9 +18,9 @@ public:
void addFeature(TextBucket &bucket, const std::vector<Coordinate> &line,
const BucketGeometryDescription &info,
const IndexedFaces &faces,
const GlyphPositions &face,
const Shaping &shaping);
float measureText(const IndexedFaces &faces,
float measureText(const GlyphPositions &face,
const Shaping &shaping);
private:

View File

@@ -19,6 +19,11 @@ extern const bool styleParseWarnings;
extern const bool spriteWarnings;
extern const bool renderWarnings;
extern const bool renderTree;
extern const bool labelTextMissingWarning;
extern const bool missingFontStackWarning;
extern const bool missingFontFaceWarning;
extern const bool glyphWarning;
extern const bool shapingWarning;
}

45
include/llmr/util/utf.hpp Normal file
View File

@@ -0,0 +1,45 @@
#ifndef LLMR_UTIL_UTF
#define LLMR_UTIL_UTF
#include <memory>
// g++/libstdc++ is missing c++11 codecvt support
#ifdef __linux__
#include <boost/locale.hpp>
#else
#include <codecvt>
#include <locale>
#endif
namespace llmr {
namespace util {
#ifdef __linux__
class utf8_to_utf32 {
public:
explicit utf8_to_utf32() {}
std::u32string convert(std::string const& utf8) {
return boost::locale::conv::utf_to_utf<char32_t>(utf8);
}
};
#else
class utf8_to_utf32 {
public:
explicit utf8_to_utf32()
: utf32conv_() {}
std::u32string convert(std::string const& utf8) {
return utf32conv_.from_bytes(utf8);
}
private:
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> utf32conv_;
};
#endif
}}
#endif

BIN
ios/img/Icon-40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

BIN
ios/img/Icon-40@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
ios/img/iTunesArtwork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

33
proto/glyphs.proto Normal file
View File

@@ -0,0 +1,33 @@
// Protocol Version 1
package llmr.glyphs;
option optimize_for = LITE_RUNTIME;
// Stores a glyph with metrics and optional SDF bitmap information.
message glyph {
required uint32 id = 1;
// A signed distance field of the glyph with a border of 3 pixels.
optional bytes bitmap = 2;
// Glyph metrics.
required uint32 width = 3;
required uint32 height = 4;
required sint32 left = 5;
required sint32 top = 6;
required uint32 advance = 7;
}
// Stores fontstack information and a list of faces.
message fontstack {
required string name = 1;
required string range = 2;
repeated glyph glyphs = 3;
}
message glyphs {
repeated fontstack stacks = 1;
extensions 16 to 8191;
}

View File

@@ -16,6 +16,7 @@ ensure_dep cmake
ensure_dep automake
ensure_dep autoconf
ensure_dep pkg-config
ensure_dep node
if [ ${UNAME} = 'Darwin' ]; then
ensure_dep makedepend
ensure_dep glibtool
@@ -27,10 +28,12 @@ fi
if [[ $MISSING_DEPS != "" ]]; then
if [ ${UNAME} = 'Darwin' ]; then
echo "Missing build deps: ${MISSING_DEPS}"
echo 'Please run "brew install autoconf automake libtool makedepend cmake pkg-config" and then re-run ./setup-libraries.sh'
echo 'Please run "brew install autoconf automake libtool makedepend cmake pkg-config node"'
echo 'and then re-run ./setup-libraries.sh'
elif [ ${UNAME} = 'Linux' ]; then
echo "Missing build deps: ${MISSING_DEPS}"
echo 'Please run "sudo apt-get install automake libtool xutils-dev cmake pkg-config" and then re-run ./setup-libraries.sh'
echo 'Please run "sudo apt-get install automake libtool xutils-dev cmake pkg-config libxi-dev libglu1-mesa-dev x11proto-randr-dev x11proto-xext-dev libxrandr-dev x11proto-xf86vidmode-dev libxxf86vm-dev libxcursor-dev nodejs-legacy"'
echo 'and then re-run ./setup-libraries.sh'
fi
exit 1
fi
@@ -71,11 +74,9 @@ source MacOSX.sh
if [ ! -f out/build-cpp11-libcpp-x86_64/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64/lib/libssl.a ] ; then ./scripts/build_openssl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -d out/build-cpp11-libcpp-x86_64/include/boost ] ; then ./scripts/build_boost.sh `pwd`/../../src/ `pwd`/../../linux/ `pwd`/../../common/ ; fi
if [ ! -d out/build-cpp11-libcpp-x86_64/include/boost ] ; then ./scripts/build_boost.sh `pwd`/../../src/ `pwd`/../../include/ `pwd`/../../linux/ `pwd`/../../common/ ; fi
echo ' ...done'
rm -rf out/packages
./scripts/make_universal.sh
cd ../../
@@ -86,14 +87,12 @@ cd ../../
elif [ ${UNAME} = 'Linux' ]; then
source Linux.sh
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64/lib/libssl.a ] ; then ./scripts/build_openssl.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -d out/build-cpp11-libstdcpp-gcc-x86_64/include/boost ] ; then ./scripts/build_boost.sh `pwd`/../../src/ `pwd`/../../linux/ `pwd`/../../common/ ; fi
rm -rf out/packages
if [ ! -d out/build-cpp11-libstdcpp-gcc-x86_64/include/boost ] ; then ./scripts/build_boost.sh `pwd`/../../src/ `pwd`/../../include/ `pwd`/../../linux/ `pwd`/../../common/ ; fi
cd ../../
./configure \

View File

@@ -2152,13 +2152,13 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
else if (e1->PolyTyp != e2->PolyTyp)
{
//toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ...
if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 &&
if ((e1->WindDelta == 0) && std::abs(e2->WindCnt) == 1 &&
(m_ClipType != ctUnion || e2->WindCnt2 == 0))
{
AddOutPt(e1, Pt);
if (e1Contributing) e1->OutIdx = Unassigned;
}
else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) &&
else if ((e2->WindDelta == 0) && (std::abs(e1->WindCnt) == 1) &&
(m_ClipType != ctUnion || e1->WindCnt2 == 0))
{
AddOutPt(e2, Pt);

View File

@@ -22,7 +22,7 @@ GlyphAtlas::~GlyphAtlas() {
}
Rect<uint16_t> GlyphAtlas::addGlyph(uint64_t tile_id, const std::string& face_name,
const VectorTileGlyph& glyph) {
const SDFGlyph& glyph) {
std::lock_guard<std::mutex> lock(mtx);
// Use constant value for now.

View File

@@ -163,7 +163,7 @@ void Map::setup() {
sources.emplace("outdoors",
std::unique_ptr<Source>(new Source(*this,
painter,
"http://api-maps-gl.tilestream.net/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/%d/%d/%d.gl.pbf",
"http://a.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/%d/%d/%d.vector.pbf",
Source::Type::vector,
{{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }},
512,
@@ -548,7 +548,6 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
painter.setStrata(i * strata_thickness);
renderLayer(*it, Opaque);
}
// painter.endPass();
if (debug::renderTree) {
std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
}
@@ -566,7 +565,6 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
painter.setStrata(i * strata_thickness);
renderLayer(*it, Translucent);
}
// painter.endPass();
if (debug::renderTree) {
std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
}

View File

@@ -9,20 +9,22 @@
#include <llmr/util/raster.hpp>
#include <llmr/util/constants.hpp>
#include <llmr/geometry/glyph_atlas.hpp>
#include <llmr/text/glyph_store.hpp>
#include <llmr/text/glyph.hpp>
#include <llmr/util/std.hpp>
#include <llmr/util/utf.hpp>
using namespace llmr;
TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, SpriteAtlas &spriteAtlas)
TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, GlyphStore &glyphStore, SpriteAtlas &spriteAtlas)
: vector_data(pbf((const uint8_t *)data.data(), data.size())),
tile(tile),
style(style),
glyphAtlas(glyphAtlas),
glyphStore(glyphStore),
spriteAtlas(spriteAtlas),
placement(tile.id.z) {
parseGlyphs();
parseStyleLayers(style.layers);
}
@@ -30,17 +32,13 @@ bool TileParser::obsolete() const {
return tile.state == TileData::State::obsolete;
}
void TileParser::parseGlyphs() {
for (const std::pair<std::string, const VectorTileFace> pair : vector_data.faces) {
const std::string &name = pair.first;
const VectorTileFace &face = pair.second;
GlyphPositions &glyphs = faces[name];
for (const VectorTileGlyph &glyph : face.glyphs) {
const Rect<uint16_t> rect =
glyphAtlas.addGlyph(tile.id.to_uint64(), name, glyph);
glyphs.emplace(glyph.id, Glyph{rect, glyph.metrics});
}
void TileParser::addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face) {
std::map<uint32_t, SDFGlyph> sdfs = fontStack.getSDFs();
// Loop through all characters and add glyph to atlas, positions.
for (uint32_t chr : string) {
const SDFGlyph sdf = sdfs[chr];
const Rect<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf);
face.emplace(chr, Glyph{rect, sdf.metrics});
}
}
@@ -162,30 +160,79 @@ std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& laye
return obsolete() ? nullptr : std::move(bucket);
}
typedef std::pair<uint16_t, uint16_t> GlyphRange;
std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) {
// Determine the correct text stack.
if (!layer.shaping.size()) {
return nullptr;
}
// TODO: currently hardcoded to use the first font stack.
const std::map<Value, Shaping>& shaping = layer.shaping.begin()->second;
const Faces& const_faces = faces;
IndexedFaces faces;
for (const std::string& face : layer.faces) {
auto it = const_faces.find(face);
if (it == const_faces.end()) {
// This layer references an unknown face.
return nullptr;
}
faces.push_back(&it->second);
}
std::unique_ptr<TextBucket> bucket = std::make_unique<TextBucket>(
tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement);
addBucketFeatures(bucket, layer, bucket_desc, faces, shaping);
util::utf8_to_utf32 ucs4conv;
// Determine and load glyph ranges
{
std::set<GlyphRange> ranges;
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
for (const pbf& feature_pbf : filtered_layer) {
if (obsolete()) return nullptr;
VectorTileFeature feature { feature_pbf, layer };
auto it_prop = feature.properties.find(bucket_desc.geometry.field);
if (it_prop == feature.properties.end()) {
// feature does not have the correct property
if (debug::labelTextMissingWarning) {
fprintf(stderr, "[WARNING] feature doesn't have property '%s' required for labelling\n", bucket_desc.geometry.field.c_str());
}
continue;
}
const std::u32string string = ucs4conv.convert(toString(it_prop->second));
// Loop through all characters of this text and collect unique codepoints.
for (uint32_t chr : string) {
ranges.insert(getGlyphRange(chr));
}
}
glyphStore.waitForGlyphRanges(bucket_desc.geometry.font, ranges);
}
// Create a copy!
const FontStack &fontStack = glyphStore.getFontStack(bucket_desc.geometry.font);
std::map<Value, Shaping> shaping;
GlyphPositions face;
// Shape and place all labels.
{
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
for (const pbf& feature_pbf : filtered_layer) {
if (obsolete()) return nullptr;
VectorTileFeature feature { feature_pbf, layer };
auto it_prop = feature.properties.find(bucket_desc.geometry.field);
if (it_prop == feature.properties.end()) {
// feature does not have the correct property
if (debug::labelTextMissingWarning) {
fprintf(stderr, "[WARNING] feature doesn't have property '%s' required for labelling\n", bucket_desc.geometry.field.c_str());
}
continue;
}
const std::u32string string = ucs4conv.convert(toString(it_prop->second));
// Shape labels.
const Shaping shaped = fontStack.getShaping(string);
shaping.emplace(toString(it_prop->second), shaped);
// Place labels.
addGlyph(tile.id.to_uint64(), bucket_desc.geometry.font, string, fontStack, glyphAtlas, face);
}
}
// It looks like nearly the same interface through the rest
// of the stack.
addBucketFeatures(bucket, layer, bucket_desc, face, shaping);
return std::move(bucket);
}

View File

@@ -60,8 +60,8 @@ void Transform::_moveBy(const double dx, const double dy, const time duration) {
// Un-rotate when rotated and panning far enough to show off-world in corners.
double w = final.scale * util::tileSize / 2;
double m = std::sqrt(pow((current.width / 2), 2) + pow((current.height / 2), 2));
double x = std::abs(sqrt(pow(final.x, 2) + pow(final.y, 2)));
double m = std::sqrt(std::pow((current.width / 2), 2) + pow((current.height / 2), 2));
double x = std::abs(sqrt(std::pow(final.x, 2) + std::pow(final.y, 2)));
if (current.angle && w - x < m) _setAngle(0, 500_milliseconds, true);
if (duration == 0) {

View File

@@ -22,7 +22,7 @@ void VectorTileData::parse() {
// Parsing creates state that is encapsulated in TileParser. While parsing,
// the TileParser object writes results into this objects. All other state
// is going to be discarded afterwards.
TileParser parser(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getSpriteAtlas());
TileParser parser(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getGlyphStore(), map.getSpriteAtlas());
} catch (const std::exception& ex) {
fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
cancel();

View File

@@ -12,25 +12,33 @@ Request::Request(const std::string &url,
res(std::make_unique<Response>(callback)),
cancelled(false),
loop(loop) {
// Add a check handle without a callback to keep the default loop running.
// We don't have a real handler attached to the default loop right from the
// beginning, because we're using asynchronous messaging to perform the actual
// request in the request thread. Only after the request is complete, we
// create an actual work request that is attached to the default loop.
async = new uv_async_t();
async->data = new std::unique_ptr<Response>();
uv_async_init(loop, async, complete);
if (loop) {
// Add a check handle without a callback to keep the default loop running.
// We don't have a real handler attached to the default loop right from the
// beginning, because we're using asynchronous messaging to perform the actual
// request in the request thread. Only after the request is complete, we
// create an actual work request that is attached to the default loop.
async = new uv_async_t();
async->data = new std::unique_ptr<Response>();
uv_async_init(loop, async, complete);
}
}
Request::~Request() {
}
void Request::complete() {
// We're scheduling the response callback to be invoked in the event loop.
// Since the Request object will be deleted before the callback is invoked,
// we move over the Response object to be owned by the async handle.
((std::unique_ptr<Response> *)async->data)->swap(res);
uv_async_send(async);
if (loop) {
// We're scheduling the response callback to be invoked in the event loop.
// Since the Request object will be deleted before the callback is invoked,
// we move over the Response object to be owned by the async handle.
((std::unique_ptr<Response> *)async->data)->swap(res);
uv_async_send(async);
} else {
// We're calling the response callback immediately. We're currently on an
// arbitrary thread, but that's okay.
res->callback(res.get());
}
}
void Request::complete(uv_async_t *async) {

View File

@@ -144,15 +144,19 @@ void Painter::clear() {
}
void Painter::setOpaque() {
pass = Opaque;
glDisable(GL_BLEND);
depthMask(true);
if (pass != Opaque) {
pass = Opaque;
glDisable(GL_BLEND);
depthMask(true);
}
}
void Painter::setTranslucent() {
pass = Translucent;
glEnable(GL_BLEND);
depthMask(false);
if (pass != Translucent) {
pass = Translucent;
glEnable(GL_BLEND);
depthMask(false);
}
}
void Painter::setStrata(float value) {

View File

@@ -180,10 +180,10 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons
return vtxMatrix;
}();
pass = Opaque;
setOpaque();
renderFill(bucket, modifiedProperties, id, vtxMatrix);
pass = Translucent;
setTranslucent();
renderFill(bucket, modifiedProperties, id, vtxMatrix);

View File

@@ -27,8 +27,6 @@ void Painter::renderPrerenderedTexture(Bucket &bucket, const GenericProperties &
const int buffer = 4096 * properties.prerenderBuffer;
// draw the texture on a quad
depthMask(false);
useProgram(rasterShader->program);
rasterShader->setMatrix(matrix);
rasterShader->setOpacity(1);
@@ -42,6 +40,4 @@ void Painter::renderPrerenderedTexture(Bucket &bucket, const GenericProperties &
bucket.prerendered->bindTexture();
coveringRasterArray.bind(*rasterShader, tileStencilBuffer, BUFFER_OFFSET(0));
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
depthMask(true);
}

View File

@@ -7,6 +7,8 @@
#include <llmr/style/style.hpp>
#include <llmr/map/vector_tile.hpp>
#include <llmr/text/placement.hpp>
#include <llmr/text/glyph_store.hpp>
#include <llmr/util/constants.hpp>
#include <llmr/util/math.hpp>
#include <llmr/platform/gl.hpp>
@@ -98,17 +100,23 @@ void TextBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
};
void TextBucket::addFeature(const VectorTileFeature &feature,
const IndexedFaces &faces,
const GlyphPositions &face,
const std::map<Value, Shaping> &shapings) {
auto it_prop = feature.properties.find(geom_desc.field);
if (it_prop == feature.properties.end()) {
// feature does not have the correct property
if (debug::labelTextMissingWarning) {
fprintf(stderr, "[WARNING] feature doesn't have property '%s' required for labelling\n", geom_desc.field.c_str());
}
return;
}
const Value &value = it_prop->second;
auto it_shaping = shapings.find(value);
auto it_shaping = shapings.find(toString(value));
if (it_shaping == shapings.end()) {
if (debug::shapingWarning) {
fprintf(stderr, "[WARNING] missing shaping for '%s'\n", toString(value).c_str());
}
// we lack shaping information for this label
return;
}
@@ -127,14 +135,14 @@ void TextBucket::addFeature(const VectorTileFeature &feature,
while ((cmd = geometry.next(x, y)) != Geometry::end) {
if (cmd == Geometry::move_to) {
if (!line.empty()) {
placement.addFeature(*this, line, geom_desc, faces, shaping);
placement.addFeature(*this, line, geom_desc, face, shaping);
line.clear();
}
}
line.emplace_back(x, y);
}
if (line.size()) {
placement.addFeature(*this, line, geom_desc, faces, shaping);
placement.addFeature(*this, line, geom_desc, face, shaping);
}
}

View File

@@ -29,9 +29,11 @@ float functions::stops(float z, const std::vector<float>& stops) {
if (stops.size() % 2 != 0) return 0;
bool smaller = false;
float smaller_z, smaller_val;
float smaller_z = 0.0;
float smaller_val = 0.0;
bool larger = false;
float larger_z, larger_val;
float larger_z = 0.0;
float larger_val = 0.0;
for (uint32_t i = 0; i < stops.size(); i += 2) {
float stop_z = stops[i];

View File

@@ -35,7 +35,7 @@ void Style::cascadeProperties(GenericProperties &properties, const GenericClass&
klass.translate.get()[1].evaluate<float>(z) }};
computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
if (klass.translate_transition && klass.translate_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Translate] = klass.translate_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Translate, klass.translate_transition.get());
}
}
@@ -47,7 +47,7 @@ void Style::cascadeProperties(GenericProperties &properties, const GenericClass&
properties.opacity = klass.opacity.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
if (klass.opacity_transition && klass.opacity_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = klass.opacity_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Opacity, klass.opacity_transition.get());
}
}
@@ -120,7 +120,7 @@ void Style::cascade(float z) {
fill.fill_color = layer.fill_color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::FillColor] = class_name;
if (layer.fill_color_transition && layer.fill_color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::FillColor] = layer.fill_color_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::FillColor, layer.fill_color_transition.get());
}
}
@@ -128,7 +128,7 @@ void Style::cascade(float z) {
fill.stroke_color = layer.stroke_color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::StrokeColor] = class_name;
if (layer.stroke_color_transition && layer.stroke_color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::StrokeColor] = layer.stroke_color_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::StrokeColor, layer.stroke_color_transition.get());
}
}
@@ -150,7 +150,7 @@ void Style::cascade(float z) {
stroke.width = layer.width.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Width] = class_name;
if (layer.width_transition && layer.width_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Width] = layer.width_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Width, layer.width_transition.get());
}
}
@@ -159,7 +159,7 @@ void Style::cascade(float z) {
stroke.offset = layer.offset.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Offset] = class_name;
if (layer.offset_transition && layer.offset_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Offset] = layer.offset_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Offset, layer.offset_transition.get());
}
}
@@ -167,7 +167,7 @@ void Style::cascade(float z) {
stroke.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, layer.color_transition.get());
}
}
@@ -176,7 +176,7 @@ void Style::cascade(float z) {
layer.dash_array.get()[1].evaluate<float>(z) }};
computed.effective_classes[layer_name][TransitionablePropertyKey::DashArray] = class_name;
if (layer.dash_array_transition && layer.dash_array_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::DashArray] = layer.dash_array_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::DashArray, layer.dash_array_transition.get());
}
}
}
@@ -194,7 +194,7 @@ void Style::cascade(float z) {
icon.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, layer.color_transition.get());
}
}
@@ -210,7 +210,7 @@ void Style::cascade(float z) {
icon.radius = layer.radius.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Radius] = class_name;
if (layer.radius_transition && layer.radius_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Radius] = layer.radius_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Radius, layer.radius_transition.get());
}
}
@@ -218,7 +218,7 @@ void Style::cascade(float z) {
icon.blur = layer.blur.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Blur] = class_name;
if (layer.blur_transition && layer.blur_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Blur] = layer.blur_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Blur, layer.blur_transition.get());
}
}
}
@@ -236,7 +236,7 @@ void Style::cascade(float z) {
text.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, layer.color_transition.get());
}
}
@@ -248,7 +248,7 @@ void Style::cascade(float z) {
text.halo = layer.halo.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Halo] = class_name;
if (layer.halo_transition && layer.halo_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Halo] = layer.halo_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Halo, layer.halo_transition.get());
}
}
@@ -256,7 +256,7 @@ void Style::cascade(float z) {
text.halo_radius = layer.halo_radius.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloRadius] = class_name;
if (layer.halo_radius_transition && layer.halo_radius_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::HaloRadius] = layer.halo_radius_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::HaloRadius, layer.halo_radius_transition.get());
}
}
@@ -264,7 +264,7 @@ void Style::cascade(float z) {
text.halo_blur = layer.halo_blur.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloBlur] = class_name;
if (layer.halo_blur_transition && layer.halo_blur_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::HaloBlur] = layer.halo_blur_transition.get();
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::HaloBlur, layer.halo_blur_transition.get());
}
}
@@ -305,7 +305,7 @@ void Style::cascade(float z) {
computed.background.color = sheetClass.background.color.get();
computed.effective_classes["background"][TransitionablePropertyKey::Color] = class_name;
if (sheetClass.background.color_transition && sheetClass.background.color_transition.get().duration) {
properties_to_transition["background"][TransitionablePropertyKey::Color] = sheetClass.background.color_transition.get();
properties_to_transition["background"].emplace(TransitionablePropertyKey::Color, sheetClass.background.color_transition.get());
}
}
}

View File

@@ -477,7 +477,7 @@ FunctionProperty StyleParser::parseFunction(JSVal value) {
return property;
}
PropertyTransition StyleParser::parseTransition(JSVal value, std::string property_name) {
boost::optional<PropertyTransition> StyleParser::parseTransition(JSVal value, std::string property_name) {
uint16_t duration = 0, delay = 0;
std::string transition_property = std::string("transition-").append(property_name);
if (value.HasMember(transition_property.c_str())) {
@@ -492,12 +492,11 @@ PropertyTransition StyleParser::parseTransition(JSVal value, std::string propert
}
}
PropertyTransition transition;
transition.duration = duration;
transition.delay = delay;
return transition;
if (duration || delay) {
return boost::optional<PropertyTransition>(PropertyTransition { duration, delay });
} else {
return boost::optional<PropertyTransition>();
}
}
void StyleParser::parseGenericClass(GenericClass &klass, JSVal value) {
@@ -529,7 +528,7 @@ void StyleParser::parseGenericClass(GenericClass &klass, JSVal value) {
}
if (value.HasMember("prerender-size")) {
klass.prerenderBuffer = toNumber<uint64_t>(parseValue(value["prerender-size"]));
klass.prerenderSize = toNumber<uint64_t>(parseValue(value["prerender-size"]));
}
if (value.HasMember("prerender-blur")) {

View File

@@ -53,7 +53,7 @@ Collision::Collision() : cTree(new Tree()), hTree(new Tree()) {
CollisionAnchor{m, m}, 1, {{M_PI * 2, 0}}, false, 2);
}
GlyphBox getMergedGlyphs(const PlacedGlyphs &placed_glyphs, bool horizontal,
GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, bool horizontal,
const CollisionAnchor &anchor) {
GlyphBox mergedGlyphs;
const float inf = std::numeric_limits<float>::infinity();
@@ -62,28 +62,28 @@ GlyphBox getMergedGlyphs(const PlacedGlyphs &placed_glyphs, bool horizontal,
mergedGlyphs.anchor = anchor;
CollisionRect &box = mergedGlyphs.box;
for (const PlacedGlyph &placed_glyph : placed_glyphs) {
const CollisionRect &gbox = placed_glyph.glyphBox.box;
for (const GlyphBox &glyph : boxes) {
const CollisionRect &gbox = glyph.box;
box.tl.x = util::min(box.tl.x, gbox.tl.x);
box.tl.y = util::min(box.tl.y, gbox.tl.y);
box.br.x = util::max(box.br.x, gbox.br.x);
box.br.y = util::max(box.br.y, gbox.br.y);
mergedGlyphs.minScale =
util::max(mergedGlyphs.minScale, placed_glyph.glyphBox.minScale);
util::max(mergedGlyphs.minScale, glyph.minScale);
}
return mergedGlyphs;
}
PlacementProperty Collision::place(const PlacedGlyphs &placed_glyphs,
PlacementProperty Collision::place(const GlyphBoxes &boxes,
const CollisionAnchor &anchor,
float minPlacementScale,
float maxPlacementScale, float padding,
bool horizontal, bool alwaysVisible) {
float minScale = std::numeric_limits<float>::infinity();
for (const PlacedGlyph &placed_glyph : placed_glyphs) {
minScale = util::min(minScale, placed_glyph.glyphBox.minScale);
for (const GlyphBox &glyphBox : boxes) {
minScale = util::min(minScale, glyphBox.minScale);
}
minPlacementScale = util::max(minPlacementScale, minScale);
@@ -92,11 +92,9 @@ PlacementProperty Collision::place(const PlacedGlyphs &placed_glyphs,
// for horizontal labels.
GlyphBoxes glyphs;
if (horizontal) {
glyphs.push_back(getMergedGlyphs(placed_glyphs, horizontal, anchor));
glyphs.push_back(getMergedGlyphs(boxes, horizontal, anchor));
} else {
for (const PlacedGlyph &placed_glyph : placed_glyphs) {
glyphs.push_back(placed_glyph.glyphBox);
}
glyphs = boxes;
}
// Calculate bboxes for all the glyphs

17
src/text/glyph.cpp Normal file
View File

@@ -0,0 +1,17 @@
#include <llmr/text/glyph.hpp>
namespace llmr {
// Note: this only works for the BMP
// Note: we could use a binary lookup table to get averaged constant time lookups, however,
// most of our lookups are going to be within the first 3 ranges listed here, so this is
// likely faster.
GlyphRange getGlyphRange(uint32_t glyph) {
unsigned start = (glyph/256) * 256;
unsigned end = (start + 255);
if (start > 65280) start = 65280;
if (end > 65533) end = 65533;
return { start, end };
}
}

185
src/text/glyph_store.cpp Normal file
View File

@@ -0,0 +1,185 @@
#include <llmr/text/glyph_store.hpp>
#include <llmr/util/std.hpp>
#include <llmr/util/string.hpp>
#include <llmr/util/utf.hpp>
#include <llmr/util/pbf.hpp>
#include <llmr/platform/platform.hpp>
#include <uv.h>
#include <algorithm>
namespace llmr {
void FontStack::insert(uint32_t id, const SDFGlyph &glyph) {
std::lock_guard<std::mutex> lock(mtx);
metrics.emplace(id, glyph.metrics);
bitmaps.emplace(id, glyph.bitmap);
sdfs.emplace(id, glyph);
}
const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const {
std::lock_guard<std::mutex> lock(mtx);
return metrics;
}
const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
std::lock_guard<std::mutex> lock(mtx);
return sdfs;
}
const Shaping FontStack::getShaping(const std::u32string &string) const {
std::lock_guard<std::mutex> lock(mtx);
uint32_t i = 0;
uint32_t x = 0;
Shaping shaped;
// Loop through all characters of this label and shape.
for (uint32_t chr : string) {
GlyphPlacement glyph = GlyphPlacement(0, chr, x, 0);
shaped.push_back(glyph);
i++;
x += metrics.find(chr)->second.advance;
}
return shaped;
}
GlyphPBF::GlyphPBF(const std::string &fontStack, GlyphRange glyphRange)
: future(promise.get_future().share())
{
// Load the glyph set URL
std::string url = util::sprintf<255>("http://mapbox.s3.amazonaws.com/gl-glyphs-256/%s/%d-%d.pbf", fontStack.c_str(), glyphRange.first, glyphRange.second);
// TODO: Find more reliable URL normalization function
std::replace(url.begin(), url.end(), ' ', '+');
fprintf(stderr, "%s\n", url.c_str());
platform::request_http(url, [&](platform::Response *res) {
if (res->code != 200) {
// Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
const std::string msg = util::sprintf<255>("[ERROR] failed to load glyphs (%d): %s\n", res->code, res->error_message.c_str());
promise.set_exception(std::make_exception_ptr(std::runtime_error(msg)));
} else {
// Transfer the data to the GlyphSet and signal its availability.
// Once it is available, the caller will need to call parse() to actually
// parse the data we received. We are not doing this here since this callback is being
// called from another (unknown) thread.
data.swap(res->body);
promise.set_value(*this);
}
});
}
std::shared_future<GlyphPBF &> GlyphPBF::getFuture() {
return future;
}
void GlyphPBF::parse(FontStack &stack) {
std::lock_guard<std::mutex> lock(mtx);
if (!data.size()) {
// If there is no data, this means we either haven't received any data, or
// we have already parsed the data.
return;
}
// Parse the glyph PBF
pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size());
while (glyphs_pbf.next()) {
if (glyphs_pbf.tag == 1) { // stacks
pbf fontstack_pbf = glyphs_pbf.message();
while (fontstack_pbf.next()) {
if (fontstack_pbf.tag == 3) { // glyphs
pbf glyph_pbf = fontstack_pbf.message();
SDFGlyph glyph;
while (glyph_pbf.next()) {
if (glyph_pbf.tag == 1) { // id
glyph.id = glyph_pbf.varint();
} else if (glyph_pbf.tag == 2) { // bitmap
glyph.bitmap = glyph_pbf.string();
} else if (glyph_pbf.tag == 3) { // width
glyph.metrics.width = glyph_pbf.varint();
} else if (glyph_pbf.tag == 4) { // height
glyph.metrics.height = glyph_pbf.varint();
} else if (glyph_pbf.tag == 5) { // left
glyph.metrics.left = glyph_pbf.svarint();
} else if (glyph_pbf.tag == 6) { // top
glyph.metrics.top = glyph_pbf.svarint();
} else if (glyph_pbf.tag == 7) { // advance
glyph.metrics.advance = glyph_pbf.varint();
} else {
glyph_pbf.skip();
}
}
stack.insert(glyph.id, glyph);
} else {
fontstack_pbf.skip();
}
}
} else {
glyphs_pbf.skip();
}
}
data.clear();
}
void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges) {
// We are implementing a blocking wait with futures: Every GlyphSet has a future that we are
// waiting for until it is loaded.
FontStack *stack = nullptr;
std::vector<std::shared_future<GlyphPBF &>> futures;
futures.reserve(glyphRanges.size());
{
std::lock_guard<std::mutex> lock(mtx);
auto &rangeSets = ranges[fontStack];
stack = &createFontStack(fontStack);
// Attempt to load the glyph range. If the GlyphSet already exists, we are getting back
// the same shared_future.
for (GlyphRange range : glyphRanges) {
futures.emplace_back(loadGlyphRange(fontStack, rangeSets, range));
}
}
// Now that we potentially created all GlyphSets, we are waiting for the results, one by one.
// When we get a result (or the GlyphSet is aready loaded), we are attempting to parse the
// GlyphSet.
for (std::shared_future<GlyphPBF &> &future : futures) {
future.get().parse(*stack);
}
}
std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &name, std::map<GlyphRange, std::unique_ptr<GlyphPBF>> &rangeSets, const GlyphRange range) {
auto range_it = rangeSets.find(range);
if (range_it == rangeSets.end()) {
// We don't have this glyph set yet for this font stack.
range_it = rangeSets.emplace(range, std::make_unique<GlyphPBF>(name, range)).first;
}
return range_it->second->getFuture();
}
FontStack &GlyphStore::createFontStack(const std::string &fontStack) {
auto stack_it = stacks.find(fontStack);
if (stack_it == stacks.end()) {
stack_it = stacks.emplace(fontStack, std::make_unique<FontStack>()).first;
}
return *stack_it->second.get();
}
FontStack &GlyphStore::getFontStack(const std::string &fontStack) {
std::lock_guard<std::mutex> lock(mtx);
return createFontStack(fontStack);
}
}

View File

@@ -35,21 +35,12 @@ bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; }
static const Glyph null_glyph;
inline const Glyph &getGlyph(const GlyphPlacement &placed,
const IndexedFaces &faces) {
if (placed.face < faces.size()) {
const GlyphPositions &face = *faces[placed.face];
if (&face) {
auto it = face.find(placed.glyph);
if (it != face.end()) {
return it->second;
} else {
fprintf(stderr, "glyph %d does not exist\n", placed.glyph);
}
} else {
fprintf(stderr, "face pointer is null\n");
}
const GlyphPositions &face) {
auto it = face.find(placed.glyph);
if (it != face.end()) {
return it->second;
} else {
fprintf(stderr, "face does not exist\n");
fprintf(stderr, "glyph %d does not exist\n", placed.glyph);
}
return null_glyph;
@@ -145,8 +136,9 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
}
}
PlacedGlyphs getGlyphs(Anchor &anchor, float advance, const Shaping &shaping,
const IndexedFaces &faces, float fontScale,
void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
Anchor &anchor, float advance, const Shaping &shaping,
const GlyphPositions &face, float fontScale,
bool horizontal, const std::vector<Coordinate> &line,
float maxAngleDelta, float rotate) {
// The total text advance is the width of this label.
@@ -162,12 +154,10 @@ PlacedGlyphs getGlyphs(Anchor &anchor, float advance, const Shaping &shaping,
// origin.x -= advance;
// }
PlacedGlyphs glyphs;
const uint32_t buffer = 3;
for (const GlyphPlacement &placed : shaping) {
const Glyph &glyph = getGlyph(placed, faces);
const Glyph &glyph = getGlyph(placed, face);
if (!glyph) {
// This glyph is empty and doesn't have any pixels that we'd need to
// show.
@@ -235,29 +225,32 @@ PlacedGlyphs getGlyphs(Anchor &anchor, float advance, const Shaping &shaping,
fontScale * util::max(tl.y, tr.y, bl.y, br.y)};
}
GlyphBox glyphBox = GlyphBox{
box,
// Prevent label from extending past the end of the line
util::max(instance.minScale, anchor.scale),
instance.maxScale,
instance.anchor,
horizontal};
// Remember the glyph for later insertion.
glyphs.emplace_back(PlacedGlyph{
tl, tr, bl, br, glyph.rect,
static_cast<float>(
std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI),
(2 * M_PI))),
GlyphBox{box, instance.minScale, instance.maxScale,
instance.anchor, horizontal}});
std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))),
glyphBox});
if (instance.offset == 0.0f) {
boxes.emplace_back(glyphBox);
}
}
}
// Prevent label from extending past the end of the line
for (PlacedGlyph &g : glyphs) {
g.glyphBox.minScale = util::max(g.glyphBox.minScale, anchor.scale);
}
return glyphs;
}
void Placement::addFeature(TextBucket& bucket,
const std::vector<Coordinate> &line,
const BucketGeometryDescription &info,
const IndexedFaces &faces,
const GlyphPositions &face,
const Shaping &shaping) {
const bool horizontal = info.path == TextPathType::Horizontal;
@@ -268,7 +261,7 @@ void Placement::addFeature(TextBucket& bucket,
const float fontScale =
(tileExtent / util::tileSize) / (glyphSize / info.size);
const float advance = measureText(faces, shaping);
const float advance = measureText(face, shaping);
Anchors anchors;
// fprintf(stderr, "adding feature with advance %f\n", advance);
@@ -289,27 +282,28 @@ void Placement::addFeature(TextBucket& bucket,
}
for (Anchor anchor : anchors) {
PlacedGlyphs glyphs =
getGlyphs(anchor, advance, shaping, faces, fontScale, horizontal,
PlacedGlyphs glyphs;
GlyphBoxes boxes;
getGlyphs(glyphs, boxes, anchor, advance, shaping, face, fontScale, horizontal,
line, maxAngleDelta, rotate);
PlacementProperty place =
collision.place(glyphs, anchor, anchor.scale, maxPlacementScale,
collision.place(boxes, anchor, anchor.scale, maxPlacementScale,
padding, horizontal, info.alwaysVisible);
if (place) {
bucket.addGlyphs(glyphs, place.zoom, place.rotationRange,
zoom - zOffset);
bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset);
}
}
}
float Placement::measureText(const IndexedFaces &faces,
float Placement::measureText(const GlyphPositions &face,
const Shaping &shaping) {
float advance = 0;
// TODO: advance is not calculated correctly. we should instead use the
// bounding box of the glyph placement.
for (const GlyphPlacement &shape : shaping) {
advance += getGlyph(shape, faces).metrics.advance;
advance += getGlyph(shape, face).metrics.advance;
}
return advance;

View File

@@ -7,3 +7,8 @@ const bool llmr::debug::styleParseWarnings = false;
const bool llmr::debug::spriteWarnings = false;
const bool llmr::debug::renderWarnings = false;
const bool llmr::debug::renderTree = false;
const bool llmr::debug::labelTextMissingWarning = true;
const bool llmr::debug::missingFontStackWarning = true;
const bool llmr::debug::missingFontFaceWarning = true;
const bool llmr::debug::glyphWarning = true;
const bool llmr::debug::shapingWarning = true;

View File

@@ -1,6 +1,7 @@
#include <llmr/util/threadpool.hpp>
#include <llmr/util/std.hpp>
#include <thread>
#include <memory>
using namespace llmr::util;

View File

@@ -11,6 +11,15 @@
#include <uv.h>
namespace llmr {
namespace platform {
void notify_map_change() {
// no-op
}
}}
class View : public llmr::View {
public:
void make_active() {