From ba812e52bd581d2dd17bc095dc9b4f8d79c78f42 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 16:57:00 -0400
Subject: [PATCH 01/66] initial travis test
---
.travis.yml | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 .travis.yml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..e4f9ff74
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: cpp
+compiler:
+ - gcc
+ - clang
+before_install:
+ - sudu apt-get update -qq
+ - sudu apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
+
+branches:
+ only:
+ - travis
+
+script: echo "Success"
From 1f6c61d9ce6baa31f86e0151519acd7df5f39ee8 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 17:00:13 -0400
Subject: [PATCH 02/66] typo
---
.travis.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index e4f9ff74..04d47280 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,8 +3,8 @@ compiler:
- gcc
- clang
before_install:
- - sudu apt-get update -qq
- - sudu apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
branches:
only:
From cc1688cab89f1be54ff7cbd7f749033d4f26d44b Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 17:04:54 -0400
Subject: [PATCH 03/66] Script for kicking off Travis CI
---
.travis.yml | 2 +-
scripts/travis-ci.sh | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100755 scripts/travis-ci.sh
diff --git a/.travis.yml b/.travis.yml
index 04d47280..786e1a84 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,4 +10,4 @@ branches:
only:
- travis
-script: echo "Success"
+script: ./scripts/travis-ci.sh
diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh
new file mode 100755
index 00000000..a5ea97ff
--- /dev/null
+++ b/scripts/travis-ci.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+qmake
+make
+cd tests
+cmake .
+make
+ctest
From f8abd39095b0f75aa1f94db3c6e2ecd29bcd631f Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 17:16:21 -0400
Subject: [PATCH 04/66] Get OpenCSG from PPA
---
.travis.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 786e1a84..2d526e98 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,9 @@ compiler:
- clang
before_install:
- sudo apt-get update -qq
- - sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
+ - sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
+ - echo 'yes' | sudo add-apt-repository ppa:chrysn/openscad
+ - sudo apt-get install -qq libopencsg-dev
branches:
only:
From e06dbbe7421ac2f7bd2f59710cae344665cd1bd7 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 17:22:22 -0400
Subject: [PATCH 05/66] Get OpenCSG from PPA - attempt 2
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 2d526e98..040c5dce 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ compiler:
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
- - echo 'yes' | sudo add-apt-repository ppa:chrysn/openscad
+ - echo 'yes' | sudo add-apt-repository ppa:chrysn/opencsg
- sudo apt-get install -qq libopencsg-dev
branches:
From acdf95002f919186f823effc75932814bc37eb64 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 17:32:53 -0400
Subject: [PATCH 06/66] Get OpenCSG from PPA - attempt 3
---
.travis.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 040c5dce..f8c3fa79 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,8 +5,8 @@ compiler:
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
- - echo 'yes' | sudo add-apt-repository ppa:chrysn/opencsg
- - sudo apt-get install -qq libopencsg-dev
+ - echo 'yes' | sudo add-apt-repository ppa:chrysn/openscad
+ - sudo apt-get update -qq libopencsg-dev
branches:
only:
From 2b966f92f247d50f2b3201d492ebc167302fc1bf Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 18:22:06 -0400
Subject: [PATCH 07/66] Get OpenCSG from PPA - attempt 4
---
.travis.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index f8c3fa79..7c9bdd27 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,10 +3,10 @@ compiler:
- gcc
- clang
before_install:
+ - echo 'yes' | sudo add-apt-repository ppa:chrysn/openscad
- sudo apt-get update -qq
- sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
- - echo 'yes' | sudo add-apt-repository ppa:chrysn/openscad
- - sudo apt-get update -qq libopencsg-dev
+ - sudo apt-get install -qq libopencsg-dev
branches:
only:
From d37cb95e2ede85193e3f3f5004095964cedc0d35 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 19:32:54 -0400
Subject: [PATCH 08/66] Detect errors mid-way into test run
---
scripts/travis-ci.sh | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh
index a5ea97ff..8aa5d1ef 100755
--- a/scripts/travis-ci.sh
+++ b/scripts/travis-ci.sh
@@ -1,8 +1,23 @@
#!/bin/bash
-qmake
-make
+qmake && make
+if [[ $? != 0 ]]; then
+ echo "Error building OpenSCAD executable"
+ exit 1
+fi
cd tests
-cmake .
+cmake .
+if [[ $? != 0 ]]; then
+ echo "Error configuring test suite"
+ exit 1
+fi
make
+if [[ $? != 0 ]]; then
+ echo "Error building test suite"
+ exit 1
+fi
ctest
+if [[ $? != 0 ]]; then
+ echo "Test failure"
+ exit 1
+fi
From 7d667b1025da27902f17dea7a85c55c830050c21 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 21:53:20 -0300
Subject: [PATCH 09/66] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index c22871a9..293efbbb 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+![Travis CI](https://api.travis-ci.org/openscad/openscad.png)
+
# What is OpenSCAD?
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=openscad&url=http://openscad.org&title=OpenSCAD&language=&tags=github&category=software)
From 9e55b312651f9ef94e91ea5b0fa58fff44fe17fe Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 21:14:24 -0400
Subject: [PATCH 10/66] clang not yet supported by the travis script
---
.travis.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 7c9bdd27..9442ca4f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
language: cpp
compiler:
- gcc
- - clang
before_install:
- echo 'yes' | sudo add-apt-repository ppa:chrysn/openscad
- sudo apt-get update -qq
From 83dc80ff5f1dbf30fff8d7f263d47d7f70832bd4 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 13 May 2013 21:14:34 -0400
Subject: [PATCH 11/66] Parallel build
---
scripts/travis-ci.sh | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh
index 8aa5d1ef..6f9868df 100755
--- a/scripts/travis-ci.sh
+++ b/scripts/travis-ci.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-qmake && make
+qmake && make -j4
if [[ $? != 0 ]]; then
echo "Error building OpenSCAD executable"
exit 1
@@ -11,12 +11,12 @@ if [[ $? != 0 ]]; then
echo "Error configuring test suite"
exit 1
fi
-make
+make -j4
if [[ $? != 0 ]]; then
echo "Error building test suite"
exit 1
fi
-ctest
+ctest -j8
if [[ $? != 0 ]]; then
echo "Test failure"
exit 1
From 435e0c021c5018ee5de69d3218c3e31c8ab75be5 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Wed, 30 Oct 2013 22:39:18 -0400
Subject: [PATCH 12/66] Limit parallel builds as Travis apparently runs out of
memory
---
scripts/travis-ci.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh
index 6f9868df..9d4258ae 100755
--- a/scripts/travis-ci.sh
+++ b/scripts/travis-ci.sh
@@ -11,7 +11,7 @@ if [[ $? != 0 ]]; then
echo "Error configuring test suite"
exit 1
fi
-make -j4
+make -j2
if [[ $? != 0 ]]; then
echo "Error building test suite"
exit 1
From cf9f19818ca5886275019f8e93c7fb8ec0e4bde6 Mon Sep 17 00:00:00 2001
From: Don Bright
Date: Tue, 26 Nov 2013 20:04:57 -0600
Subject: [PATCH 13/66] prevent crash in CGAL nef3. fix #issue 410 . also deal
w qmake bug re .h files
---
openscad.pro | 2 ++
src/CGALEvaluator.cc | 16 +++++++++++++---
src/CGAL_Nef_polyhedron.cc | 17 ++++++++++++++---
src/cgal.h | 8 +++++---
src/export.cc | 10 +++++++++-
5 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/openscad.pro b/openscad.pro
index fd9f4947..b38419ef 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -39,6 +39,7 @@ debug: DEFINES += DEBUG
TEMPLATE = app
INCLUDEPATH += src
+DEPENDPATH += src
# Handle custom library location.
# Used when manually installing 3rd party libraries
@@ -368,6 +369,7 @@ HEADERS += src/cgal.h \
src/PolySetCGALEvaluator.h \
src/CGALRenderer.h \
src/CGAL_Nef_polyhedron.h \
+ src/CGAL_Nef3_workaround.h \
src/cgalworker.h
SOURCES += src/cgalutils.cc \
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
index ec013153..242fe0f9 100644
--- a/src/CGALEvaluator.cc
+++ b/src/CGALEvaluator.cc
@@ -159,9 +159,19 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)
PRINT("Hull() currently requires a valid 2-manifold. Please modify your design. See http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/STL_Import_and_Export");
}
else {
- chN.p3->convert_to_Polyhedron(P);
- std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
- boost::bind(static_cast(&CGAL_Polyhedron::Vertex::point), _1));
+ bool err = false;
+ try{
+ err = nefworkaround::convert_to_Polyhedron( *(chN.p3), P );
+ //chN.p3->convert_to_Polyhedron(P);
+ } catch (...) {
+ err = true;
+ }
+ if (err) {
+ PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed");
+ } else {
+ std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
+ boost::bind(static_cast(&CGAL_Polyhedron::Vertex::point), _1));
+ }
}
}
chnode->progress_report();
diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc
index 440f4edd..4761d265 100644
--- a/src/CGAL_Nef_polyhedron.cc
+++ b/src/CGAL_Nef_polyhedron.cc
@@ -98,11 +98,22 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset()
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Polyhedron P;
- this->p3->convert_to_Polyhedron(P);
- ps = createPolySetFromPolyhedron(P);
+ bool err = nefworkaround::convert_to_Polyhedron( *(this->p3), P );
+ //this->p3->convert_to_Polyhedron(P);
+ if (err) {
+ PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed");
+ } else {
+ ps = createPolySetFromPolyhedron(P);
+ }
}
catch (const CGAL::Precondition_exception &e) {
- PRINTB("CGAL error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
+ PRINTB("CGAL Precondition error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
+ }
+ catch (const CGAL::Assertion_exception &e) {
+ PRINTB("CGAL Assertion error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
+ }
+ catch (...) {
+ PRINT("CGAL unknown error in CGAL_Nef_polyhedron::convertToPolyset()");
}
CGAL::set_error_behaviour(old_behaviour);
}
diff --git a/src/cgal.h b/src/cgal.h
index 45228be1..69c8c270 100644
--- a/src/cgal.h
+++ b/src/cgal.h
@@ -27,6 +27,7 @@ using boost::uintmax_t;
#include
#include
#include
+#include
#include
#include
#include
@@ -48,9 +49,10 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_ExactKernel2;
typedef CGAL::Polygon_2 CGAL_Poly2;
typedef CGAL::Polygon_with_holes_2 CGAL_Poly2h;
- //typedef CGAL::Cartesian CGAL_Kernel3;
-typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3;
-typedef CGAL::Exact_predicates_exact_constructions_kernel::FT NT3;
+typedef CGAL::Gmpq NT3;
+typedef CGAL::Cartesian CGAL_Kernel3;
+//typedef CGAL::Exact_predicates_exact_constructions_kernel::FT NT3;
+//typedef CGAL::Exact_predicates_exact_constructions_kernel CGAL_Kernel3;
typedef CGAL::Nef_polyhedron_3 CGAL_Nef_polyhedron3;
typedef CGAL_Nef_polyhedron3::Aff_transformation_3 CGAL_Aff_transformation;
diff --git a/src/export.cc b/src/export.cc
index ec6e576a..cef323e8 100644
--- a/src/export.cc
+++ b/src/export.cc
@@ -42,7 +42,12 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output)
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Polyhedron P;
- root_N->p3->convert_to_Polyhedron(P);
+ //root_N->p3->convert_to_Polyhedron(P);
+ bool err = nefworkaround::convert_to_Polyhedron( *(root_N->p3), P );
+ if (err) {
+ PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed");
+ return;
+ }
typedef CGAL_Polyhedron::Vertex Vertex;
typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
@@ -114,6 +119,9 @@ void export_stl(CGAL_Nef_polyhedron *root_N, std::ostream &output)
catch (const CGAL::Assertion_exception &e) {
PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what());
}
+ catch (...) {
+ PRINT("CGAL unknown error in CGAL_Nef_polyhedron3::convert_to_Polyhedron()");
+ }
CGAL::set_error_behaviour(old_behaviour);
}
From faf008ce24e5169dcfe75d90bfbc988abdfd7f93 Mon Sep 17 00:00:00 2001
From: Don Bright
Date: Tue, 26 Nov 2013 20:29:29 -0600
Subject: [PATCH 14/66] simplify nef polyhedron code. attempt to add test for
bug
---
src/CGAL_Nef_polyhedron.cc | 26 ++++++++++++--------------
tests/CMakeLists.txt | 1 +
2 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/src/CGAL_Nef_polyhedron.cc b/src/CGAL_Nef_polyhedron.cc
index 4761d265..49b9a534 100644
--- a/src/CGAL_Nef_polyhedron.cc
+++ b/src/CGAL_Nef_polyhedron.cc
@@ -96,24 +96,22 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset()
}
else if (this->dim == 3) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
+ bool err = true;
+ std::string errmsg("");
+ CGAL_Polyhedron P;
try {
- CGAL_Polyhedron P;
- bool err = nefworkaround::convert_to_Polyhedron( *(this->p3), P );
+ err = nefworkaround::convert_to_Polyhedron( *(this->p3), P );
//this->p3->convert_to_Polyhedron(P);
- if (err) {
- PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed");
- } else {
- ps = createPolySetFromPolyhedron(P);
- }
}
- catch (const CGAL::Precondition_exception &e) {
- PRINTB("CGAL Precondition error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
+ catch (const CGAL::Failure_exception &e) {
+ err = true;
+ errmsg = std::string(e.what());
}
- catch (const CGAL::Assertion_exception &e) {
- PRINTB("CGAL Assertion error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
- }
- catch (...) {
- PRINT("CGAL unknown error in CGAL_Nef_polyhedron::convertToPolyset()");
+ if (err) {
+ PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed.");
+ if (errmsg!="") PRINTB("ERROR: %s",errmsg);
+ } else {
+ ps = createPolySetFromPolyhedron(P);
}
CGAL::set_error_behaviour(old_behaviour);
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 3d3aad1e..731a418a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -800,6 +800,7 @@ list(APPEND CGALPNGTEST_FILES ${FEATURES_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_FILE
list(APPEND CGALPNGTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/transform-nan-inf-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/stl-cgal-convert_to_Polyhedron-crash.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles_dir/localfiles-compatibility-test.scad)
From fd715c6526e961cb7f3d6ba6a0563788d7d1674d Mon Sep 17 00:00:00 2001
From: Don Bright
Date: Tue, 26 Nov 2013 20:55:26 -0600
Subject: [PATCH 15/66] finish adding new test, add png for new test
---
tests/CMakeLists.txt | 5 +++++
...cgal-convert_to_Polyhedron-crash-expected.png | Bin 0 -> 4350 bytes
...cgal-convert_to_Polyhedron-crash-expected.csg | 5 +++++
3 files changed, 10 insertions(+)
create mode 100644 tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.png
create mode 100644 tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 731a418a..0477a45d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -794,6 +794,7 @@ list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles_dir/localfiles-compatibility-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allexpressions.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allfunctions.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/stl-cgal-convert_to_Polyhedron-crash.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allmodules.scad)
list(APPEND CGALPNGTEST_FILES ${FEATURES_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_FILES})
@@ -823,6 +824,10 @@ disable_tests(openscad-csgpng_child-background)
disable_tests(opencsgtest_example006 cgalpngtest_example006)
disable_tests(openscad-csgpng_example006 openscad-cgalpng_example006)
+# NefPolyhedron->Polyhedron conversion failures. No images for OpenCSG/Thrown
+disable_tests(opencsgtest_stl-cgal-convert_to_Polyhedron-crash)
+disable_tests(throwntogethertest_stl-cgal-convert_to_Polyhedron-crash)
+
# These tests only makes sense in OpenCSG mode
disable_tests(cgalpngtest_child-background
cgalpngtest_highlight-and-background-modifier
diff --git a/tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.png b/tests/regression/cgalpngtest/stl-cgal-convert_to_Polyhedron-crash-expected.png
new file mode 100644
index 0000000000000000000000000000000000000000..318cbaabb1c5aa09c123f8da6863298527c7242d
GIT binary patch
literal 4350
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
YOPsfOE;2&n5B+UezsWXd9rMh80A%9J-v9sr
literal 0
HcmV?d00001
diff --git a/tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg b/tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg
new file mode 100644
index 00000000..acad52fd
--- /dev/null
+++ b/tests/regression/dumptest/stl-cgal-convert_to_Polyhedron-crash-expected.csg
@@ -0,0 +1,5 @@
+group() {
+ render(convexity = 1) {
+ import(file = "stl-cgal-convert_to_Polyhedron-crash.stl", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
+ }
+}
From 9ea7713335122eabdd243cfcf1e5ae87a8bd23d1 Mon Sep 17 00:00:00 2001
From: Don Bright
Date: Sat, 30 Nov 2013 15:43:00 -0600
Subject: [PATCH 16/66] print errmsg for applyHull. add quotes around err msg
in version check.
---
src/CGALEvaluator.cc | 5 +++--
src/version_check.h | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
index 242fe0f9..4a052745 100644
--- a/src/CGALEvaluator.cc
+++ b/src/CGALEvaluator.cc
@@ -160,14 +160,15 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)
}
else {
bool err = false;
+ std::string errmsg("");
try{
err = nefworkaround::convert_to_Polyhedron( *(chN.p3), P );
//chN.p3->convert_to_Polyhedron(P);
- } catch (...) {
+ catch (const CGAL::Failure_exception &e) {
err = true;
}
if (err) {
- PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed");
+ PRINTB("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s", e.what());
} else {
std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
boost::bind(static_cast(&CGAL_Polyhedron::Vertex::point), _1));
diff --git a/src/version_check.h b/src/version_check.h
index fbea077f..be52e61d 100644
--- a/src/version_check.h
+++ b/src/version_check.h
@@ -113,7 +113,7 @@ a time, to avoid confusion.
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if GCC_VERSION == 40802
-#error OpenSCAD isn't compatible with gcc 4.8.2. Please try a different version
+#error "OpenSCAD isnt compatible with gcc 4.8.2. Please try a different version"
#endif
#endif // OPENSCAD_SKIP_VERSION_CHECK
From 791a49b9e8489818e41deae2b1d4ba2b6ff50e5f Mon Sep 17 00:00:00 2001
From: Don Bright
Date: Sat, 30 Nov 2013 17:26:50 -0600
Subject: [PATCH 17/66] build bug fix
---
src/CGALEvaluator.cc | 7 +-
src/CGAL_Nef3_workaround.h | 352 +++++++++++++++++++++++++++++++++++++
2 files changed, 356 insertions(+), 3 deletions(-)
create mode 100644 src/CGAL_Nef3_workaround.h
diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc
index 4a052745..86118d78 100644
--- a/src/CGALEvaluator.cc
+++ b/src/CGALEvaluator.cc
@@ -161,14 +161,15 @@ CGAL_Nef_polyhedron CGALEvaluator::applyHull(const CgaladvNode &node)
else {
bool err = false;
std::string errmsg("");
- try{
+ try {
err = nefworkaround::convert_to_Polyhedron( *(chN.p3), P );
//chN.p3->convert_to_Polyhedron(P);
- catch (const CGAL::Failure_exception &e) {
+ } catch (const CGAL::Failure_exception &e) {
err = true;
+ errmsg = std::string(e.what());
}
if (err) {
- PRINTB("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s", e.what());
+ PRINTB("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed. %s", errmsg);
} else {
std::transform(P.vertices_begin(), P.vertices_end(), std::back_inserter(points3d),
boost::bind(static_cast(&CGAL_Polyhedron::Vertex::point), _1));
diff --git a/src/CGAL_Nef3_workaround.h b/src/CGAL_Nef3_workaround.h
new file mode 100644
index 00000000..c2482ac4
--- /dev/null
+++ b/src/CGAL_Nef3_workaround.h
@@ -0,0 +1,352 @@
+// Copyright (c) 1997-2002,2005 Max-Planck-Institute Saarbruecken (Germany).
+// All rights reserved.
+//
+// This file is part of CGAL (www.cgal.org).
+// You can redistribute it and/or modify it under the terms of the GNU
+// General Public License as published by the Free Software Foundation,
+// either version 3 of the License, or (at your option) any later version.
+//
+// Licensees holding a valid commercial license may use this file in
+// accordance with the commercial license agreement provided with the software.
+//
+// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/releases/CGAL-4.0-branch/Nef_3/include/CGAL/Nef_helper_3.h $
+// $Id: Nef_helper_3.h 67117 2012-01-13 18:14:48Z lrineau $
+//
+//
+// Author(s) : Michael Seel
+// Miguel Granados
+// Susan Hert
+// Lutz Kettner
+// Ralf Osbild
+// Peter Hachenberger
+
+/*
+ modified by don bright for OpenSCAD, 2013.
+
+This works around issue #410, where CGAL's Nef_Polyhedron3.convert_to_Polyhedron
+throws an uncatchable exception, due to an CGAL_Assertion being thrown in
+Polyhedron Incremental Builder's destructor while a Triangulation exception
+is still active, resulting in program termination (crashing).
+
+The purpose here is not to improve/change the way CGAL's Nef code works,
+but instead to tweak it slightly to prevent OpenSCAD from crashing. The
+behavior of the code should otherwise be exactly the same as CGAL's standard
+code.
+
+This file was created by copying three sections
+from CGAL's Nef_polyhedron3.h that were protected/private:
+
+ Triangulation_handler2
+ Build_Polyhedron
+ convert_to_polyhedron
+
+Very small code changes have been made. First, there are many template
+type specifiers added to enable the movement of the code to the outside
+of the Nef Polyhedron class. Second, there is a try{}catch(...){} block
+added in the Builder around the Triangulation code. Third, there is an error
+variable added for non-Exception communication with the caller.
+
+Eventually, if CGAL itself is updated and the update is widely
+distributed, this file may become obsolete and can be deleted from OpenSCAD
+
+*/
+
+
+#ifndef _CGAL_NEF3_WORKAROUND_H
+#define _CGAL_NEF3_WORKAROUND_H
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "printutils.h"
+
+namespace nefworkaround {
+
+template
+class Triangulation_handler2 {
+
+ typedef typename CGAL::Triangulation_vertex_base_2 Vb;
+ typedef typename CGAL::Constrained_triangulation_face_base_2 Fb;
+ typedef typename CGAL::Triangulation_data_structure_2 TDS;
+ typedef typename CGAL::Constrained_triangulation_2 CT;
+
+ typedef typename CT::Face_handle Face_handle;
+ typedef typename CT::Vertex_handle CTVertex_handle;
+ typedef typename CT::Finite_faces_iterator Finite_face_iterator;
+ typedef typename CT::Edge Edge;
+ CT ct;
+ CGAL::Unique_hash_map visited;
+ CGAL::Unique_hash_map ctv2v;
+ Finite_face_iterator fi;
+ typename Nef::Plane_3 supporting_plane;
+
+ public:
+ Triangulation_handler2(typename Nef::Halffacet_const_handle f) :
+ visited(false), supporting_plane(f->plane()) {
+
+ typename Nef::Halffacet_cycle_const_iterator fci;
+ for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
+ if(fci.is_shalfedge()) {
+ typename Nef::SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
+ CGAL_For_all(sfc,send) {
+ CGAL_NEF_TRACEN(" insert point" << sfc->source()->source()->point());
+ CTVertex_handle ctv = ct.insert(sfc->source()->source()->point());
+ ctv2v[ctv] = sfc->source()->source();
+ }
+ }
+ }
+
+ for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
+ if(fci.is_shalfedge()) {
+ typename Nef::SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
+ CGAL_For_all(sfc,send) {
+ CGAL_NEF_TRACEN(" insert constraint" << sfc->source()->source()->point()
+ << "->" << sfc->source()->twin()->source()->point());
+ ct.insert_constraint(sfc->source()->source()->point(),
+ sfc->source()->twin()->source()->point());
+ }
+ }
+ }
+ CGAL_assertion(ct.is_valid());
+
+ CGAL_NEF_TRACEN("number of finite triangles " << ct.number_of_faces());
+
+ typename CT::Face_handle infinite = ct.infinite_face();
+ typename CT::Vertex_handle ctv = infinite->vertex(1);
+ if(ct.is_infinite(ctv)) ctv = infinite->vertex(2);
+ CGAL_assertion(!ct.is_infinite(ctv));
+
+ typename CT::Face_handle opposite;
+ typename CT::Face_circulator vc(ctv,infinite);
+ do { opposite = vc++;
+ } while(!ct.is_constrained(typename CT::Edge(vc,vc->index(opposite))));
+ typename CT::Face_handle first = vc;
+
+ CGAL_assertion(!ct.is_infinite(first));
+ traverse_triangulation(first, first->index(opposite));
+
+ fi = ct.finite_faces_begin();
+ }
+
+ void traverse_triangulation(Face_handle f, int parent) {
+ visited[f] = true;
+ if(!ct.is_constrained(Edge(f,ct.cw(parent))) && !visited[f->neighbor(ct.cw(parent))]) {
+ Face_handle child(f->neighbor(ct.cw(parent)));
+ traverse_triangulation(child, child->index(f));
+ }
+ if(!ct.is_constrained(Edge(f,ct.ccw(parent))) && !visited[f->neighbor(ct.ccw(parent))]) {
+ Face_handle child(f->neighbor(ct.ccw(parent)));
+ traverse_triangulation(child, child->index(f));
+ }
+ }
+
+ template
+ bool get_next_triangle(Triangle_3& tr) {
+ while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
+ if(fi == ct.finite_faces_end()) return false;
+ tr = Triangle_3(fi->vertex(0)->point(), fi->vertex(1)->point(), fi->vertex(2)->point());
+ ++fi;
+ return true;
+ }
+
+ bool same_orientation(typename Nef::Plane_3 p1) const {
+ if(p1.a() != 0)
+ return CGAL::sign(p1.a()) == CGAL::sign(supporting_plane.a());
+ if(p1.b() != 0)
+ return CGAL::sign(p1.b()) == CGAL::sign(supporting_plane.b());
+ return CGAL::sign(p1.c()) == CGAL::sign(supporting_plane.c());
+ }
+
+ template
+ void handle_triangles(PIB& pib, Index& VI) {
+ while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
+ while(fi != ct.finite_faces_end()) {
+ typename Nef::Plane_3 plane(fi->vertex(0)->point(),
+ fi->vertex(1)->point(),
+ fi->vertex(2)->point());
+ pib.begin_facet();
+ if(same_orientation(plane)) {
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
+ } else {
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
+ pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
+ }
+ pib.end_facet();
+ do {
+ ++fi;
+ } while(fi != ct.finite_faces_end() && visited[fi] == false);
+ }
+ }
+};
+
+
+
+
+
+
+
+
+template
+class Build_polyhedron : public CGAL::Modifier_base {
+public:
+ bool error; // added for OpenSCAD
+ class Visitor {
+ typedef typename CGAL::Projection_traits_xy_3 XY;
+ typedef typename CGAL::Projection_traits_yz_3 YZ;
+ typedef typename CGAL::Projection_traits_xz_3 XZ;
+
+ const CGAL::Object_index& VI;
+ CGAL::Polyhedron_incremental_builder_3& B;
+ const typename Nef::SNC_const_decorator& D;
+
+ public:
+ bool error;//added for OpenSCAD
+ Visitor(CGAL::Polyhedron_incremental_builder_3& BB,
+ const typename Nef::SNC_const_decorator& sd,
+ CGAL::Object_index& vi) : VI(vi), B(BB), D(sd), error(false) {}
+
+ void visit(typename Nef::Halffacet_const_handle opposite_facet) {
+
+ CGAL_NEF_TRACEN("Build_polyhedron: visit facet " << opposite_facet->plane());
+
+ CGAL_assertion(Nef::Infi_box::is_standard(opposite_facet->plane()));
+
+ typename Nef::SHalfedge_const_handle se;
+ typename Nef::Halffacet_cycle_const_iterator fc;
+
+ typename Nef::Halffacet_const_handle f = opposite_facet->twin();
+
+ typename Nef::SHalfedge_around_facet_const_circulator
+ sfc1(f->facet_cycles_begin()), sfc2(sfc1);
+
+ if(++f->facet_cycles_begin() != f->facet_cycles_end() ||
+ ++(++(++sfc1)) != sfc2) {
+ typename Nef::Vector_3 orth = f->plane().orthogonal_vector();
+ int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1;
+ c = CGAL::abs(orth[2]) > CGAL::abs(orth[c]) ? 2 : c;
+
+ try{ // added for OpenSCAD
+ if(c == 0) {
+ Triangulation_handler2 th(f);
+ th.handle_triangles(B, VI);
+ } else if(c == 1) {
+ Triangulation_handler2 th(f);
+ th.handle_triangles(B, VI);
+ } else if(c == 2) {
+ Triangulation_handler2 th(f);
+ th.handle_triangles(B, VI);
+ } else
+ CGAL_error_msg( "wrong value");
+ } catch(...) { // added for OpenSCAD
+ PRINT("ERROR: CGAL NefPolyhedron Triangulation failed"); // added for OpenSCAD
+ this->error=true; //added for OpenSCAD
+ } // added for OpenSCAD
+ } else {
+
+ B.begin_facet();
+ fc = f->facet_cycles_begin();
+ se = typename Nef::SHalfedge_const_handle(fc);
+ CGAL_assertion(se!=0);
+ typename Nef::SHalfedge_around_facet_const_circulator hc_start(se);
+ typename Nef::SHalfedge_around_facet_const_circulator hc_end(hc_start);
+ CGAL_For_all(hc_start,hc_end) {
+ CGAL_NEF_TRACEN(" add vertex " << hc_start->source()->center_vertex()->point());
+ B.add_vertex_to_facet(VI[hc_start->source()->center_vertex()]);
+ }
+ B.end_facet();
+ }
+ }
+
+ void visit(typename Nef::SFace_const_handle) {}
+ void visit(typename Nef::Halfedge_const_handle) {}
+ void visit(typename Nef::Vertex_const_handle) {}
+ void visit(typename Nef::SHalfedge_const_handle) {}
+ void visit(typename Nef::SHalfloop_const_handle) {}
+ };
+
+ public:
+
+ const typename Nef::SNC_const_decorator& scd;
+ CGAL::Object_index VI;
+
+ Build_polyhedron(const typename Nef::SNC_const_decorator& s) : error(false),
+ scd(s), VI(s.vertices_begin(),s.vertices_end(),'V') {}
+
+ void operator()( HDS& hds) {
+ CGAL::Polyhedron_incremental_builder_3 B(hds, true);
+
+ int skip_volumes;
+ if(Nef::Infi_box::extended_kernel()) {
+ B.begin_surface(scd.number_of_vertices()-8,
+ scd.number_of_facets()-6,
+ scd.number_of_edges()-12);
+ skip_volumes = 2;
+ }
+ else {
+ B.begin_surface(scd.number_of_vertices(),
+ 2*scd.number_of_vertices()-4,
+ 3*scd.number_of_vertices()-6);
+ skip_volumes = 1;
+ }
+
+ int vertex_index = 0;
+ typename Nef::Vertex_const_iterator v;
+ CGAL_forall_vertices(v,scd) {
+ if(Nef::Infi_box::is_standard(v->point())) {
+ VI[v]=vertex_index++;
+ B.add_vertex(v->point());
+ }
+ }
+
+ Visitor V(B,scd,VI);
+ typename Nef::Volume_const_handle c;
+ CGAL_forall_volumes(c,scd)
+ if(skip_volumes-- <= 0) {
+ scd.visit_shell_objects(typename Nef:: SFace_const_handle(c->shells_begin()),V);
+ }
+ B.end_surface();
+ this->error=B.error()||V.error; // added for OpenSCAD
+ if (B.error()) B.rollback(); // added for OpenSCAD
+ }
+
+};
+
+template
+bool convert_to_Polyhedron( const CGAL::Nef_polyhedron_3 &N, CGAL::Polyhedron_3 &P )
+{
+ // several lines here added for OpenSCAD
+ typedef typename CGAL::Nef_polyhedron_3 Nef3;
+ typedef typename CGAL::Polyhedron_3 Polyhedron;
+ typedef typename Polyhedron::HalfedgeDS HalfedgeDS;
+ CGAL_precondition(N.is_simple());
+ P.clear();
+ Build_polyhedron bp(N);
+ P.delegate(bp);
+ return bp.error;
+}
+
+
+
+
+
+} //namespace nefworkaround
+
+
+
+
+#endif
+
From 69cbb17497ab52620e39874a2323db1aadf6dcbe Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Wed, 4 Dec 2013 01:15:19 -0500
Subject: [PATCH 18/66] Killed warnings
---
src/CocoaUtils.mm | 4 ++--
src/modcontext.cc | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/CocoaUtils.mm b/src/CocoaUtils.mm
index 92640fdd..9856b3d7 100644
--- a/src/CocoaUtils.mm
+++ b/src/CocoaUtils.mm
@@ -8,7 +8,7 @@ void CocoaUtils::endApplication()
object:nil];
}
-void CocoaUtils::nslog(const std::string &str, void *userdata)
+void CocoaUtils::nslog(const std::string &str, void * /* userdata */)
{
- NSLog([NSString stringWithUTF8String: str.c_str()]);
+ NSLog(@"%s", str.c_str());
}
diff --git a/src/modcontext.cc b/src/modcontext.cc
index 5b480097..7941cf5c 100644
--- a/src/modcontext.cc
+++ b/src/modcontext.cc
@@ -162,7 +162,7 @@ void ModuleContext::dump(const AbstractModule *mod, const ModuleInstantiation *i
#endif
FileContext::FileContext(const class FileModule &module, const Context *parent)
- : usedlibs(module.usedlibs), ModuleContext(parent)
+ : ModuleContext(parent), usedlibs(module.usedlibs)
{
if (!module.modulePath().empty()) this->document_path = module.modulePath();
}
From f2fe074e1d947f74e34833453cc613e46e5450a6 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Wed, 4 Dec 2013 01:15:34 -0500
Subject: [PATCH 19/66] clang fix: Clang claims to be gcc
---
src/stl-utils.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/stl-utils.cc b/src/stl-utils.cc
index 790fd179..027339cc 100644
--- a/src/stl-utils.cc
+++ b/src/stl-utils.cc
@@ -1,4 +1,4 @@
-#if defined(__APPLE__) && defined(__GNUC__)
+#if defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__)
#include
From d3b82dcac0cbd6bb46c3236d1183f84b76b44748 Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Thu, 5 Dec 2013 15:56:50 +1100
Subject: [PATCH 20/66] Fix for bad boost libraries
Get this error because of a search for a non-existent library on linux64
-----------
[ 69%] Built target tests-cgal
Scanning dependencies of target cgalcachetest
[ 70%] Building CXX object
CMakeFiles/cgalcachetest.dir/cgalcachetest.cc.o
make[2]: *** No rule to make target `/usr/lib/libboost_thread.so',
needed by `cgalcachetest'. Stop.
make[1]: *** [CMakeFiles/cgalcachetest.dir/all] Error 2
make: *** [all] Error 2
[2]+ Done gedit openscad.pro (wd:
~/git/openscad_unicode)
----------
---
tests/CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0477a45d..4cc86f54 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -377,11 +377,45 @@ if("${CGAL_MAJOR_VERSION}.${CGAL_MINOR_VERSION}" VERSION_LESS 3.6)
endif()
inclusion(CGAL_DIR CGAL_INCLUDE_DIRS)
+#Get rid of bad libraries suggested for BOOST dependencies (they don't exist on some machines and cause build failures).
+#/usr/lib/libboost_thread.so;/usr/lib/libboost_system.so;
+string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_system.so" FIND_POSITION )
+if(NOT "-1" STREQUAL ${FIND_POSITION} )
+if(NOT EXISTS "/usr/lib/libboost_system.so")
+ MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_system.so -- stripping" )
+ string(REPLACE "/usr/lib/libboost_system.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+endif()
+endif()
+string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_thread.so" FIND_POSITION )
+if(NOT "-1" STREQUAL ${FIND_POSITION} )
+if(NOT EXISTS "/usr/lib/libboost_thread.so")
+ MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_thread.so -- stripping" )
+ string(REPLACE "/usr/lib/libboost_thread.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+endif()
+endif()
if(${CMAKE_CXX_COMPILER} MATCHES ".*clang.*" AND NOT ${CGAL_CXX_FLAGS_INIT} STREQUAL "" )
string(REPLACE "-frounding-math" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
string(REPLACE "--param=ssp-buffer-size=4" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
endif()
+if (NOT $ENV{OPENSCAD_LIBRARIES} STREQUAL "")
+ # Force pkg-config to look _only_ in the local library folder
+ # in case OPENSCAD_LIBRARIES is set.
+ set(ENV{PKG_CONFIG_PATH} "$ENV{OPENSCAD_LIBRARIES}/lib/pkgconfig")
+ set(ENV{PKG_CONFIG_LIBDIR} "$ENV{OPENSCAD_LIBRARIES}/lib/pkgconfig")
+endif()
+
+# Find libraries (system installed or dependency built) using pkg-config
+find_package(PkgConfig REQUIRED)
+
+#GLib-2
+pkg_search_module(GLIB2 REQUIRED glib-2.0>=2.2.0)
+#Can't use the CXXFlags directly as they are ;-separated
+string(REPLACE ";" " " GLIB2_CFLAGS "${GLIB2_CFLAGS}")
+message(STATUS "glib-2.0 found: ${GLIB2_VERSION}")
+
+add_definitions(${GLIB2_CFLAGS})
+
# Imagemagick
if (SKIP_IMAGEMAGICK)
From 0717c67c9fa894ecb08dc5de281753a00922d1ee Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Thu, 5 Dec 2013 17:56:54 +1100
Subject: [PATCH 21/66] Unicode support for strings
Add suport for using unicode strings in .scad files. Support iterating
across them/accessing them via [] and searching.
--------
Add GLIB (to build for test and normal build -- both with installed and
built locally development files).
Add support for unicode chars to length and search builtin functions and
[] for strings.
Added unicode testing functions.
Ad GLIB to library info page.
---
.gitignore | 1 +
README.md | 1 +
common.pri | 1 +
glib-2.0.pri | 38 ++++++
openscad.pro | 3 +-
scripts/check-dependencies.sh | 17 ++-
scripts/uni-build-dependencies.sh | 1 +
scripts/uni-get-dependencies.sh | 15 +--
src/AboutDialog.html | 1 +
src/PlatformUtils.cc | 3 +
src/func.cc | 49 ++++++--
src/value.cc | 18 ++-
testdata/scad/misc/search-tests-unicode.scad | 116 ++++++++++++++++++
testdata/scad/misc/string-unicode.scad | 36 ++++++
tests/CMakeLists.txt | 6 +-
.../search-tests-unicode-expected.echo | 109 ++++++++++++++++
.../echotest/string-unicode-expected.echo | 104 ++++++++++++++++
17 files changed, 497 insertions(+), 22 deletions(-)
create mode 100644 glib-2.0.pri
create mode 100644 testdata/scad/misc/search-tests-unicode.scad
create mode 100644 testdata/scad/misc/string-unicode.scad
create mode 100644 tests/regression/echotest/search-tests-unicode-expected.echo
create mode 100644 tests/regression/echotest/string-unicode-expected.echo
diff --git a/.gitignore b/.gitignore
index 50dace12..59bac496 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
/*.scad
*.dmg
+*~
*.tar*
Makefile
objects
diff --git a/README.md b/README.md
index 27f12cec..1e97e0f6 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,7 @@ Follow the instructions for the platform you're compiling on below.
* [OpenCSG (1.3.2)](http://www.opencsg.org/)
* [GLEW (1.5.4 ->)](http://glew.sourceforge.net/)
* [Eigen (3.0 - 3.2)](http://eigen.tuxfamily.org/)
+* [glib2 (2.2.0)](https://developer.gnome.org/glib/)
* [GCC C++ Compiler (4.2 ->)](http://gcc.gnu.org/)
* [Bison (2.4)](http://www.gnu.org/software/bison/)
* [Flex (2.5.35)](http://flex.sourceforge.net/)
diff --git a/common.pri b/common.pri
index 7153ded7..696c8b1d 100644
--- a/common.pri
+++ b/common.pri
@@ -11,3 +11,4 @@ include(opencsg.pri)
include(glew.pri)
include(eigen.pri)
include(boost.pri)
+include(glib-2.0.pri)
\ No newline at end of file
diff --git a/glib-2.0.pri b/glib-2.0.pri
new file mode 100644
index 00000000..0fbc4e2d
--- /dev/null
+++ b/glib-2.0.pri
@@ -0,0 +1,38 @@
+# Detect glib-2.0, then use this priority list to determine
+# which library to use:
+#
+# Priority
+# 1. GLIB2_INCLUDEPATH / GLIB2_LIBPATH (qmake parameter, not checked it given on commandline)
+# 2. OPENSCAD_LIBRARIES (environment variable)
+# 3. system's standard include paths from pkg-config
+
+glib-2.0 {
+
+# read environment variables
+OPENSCAD_LIBRARIES_DIR = $$(OPENSCAD_LIBRARIES)
+GLIB2_DIR = $$(GLIB2DIR)
+
+!isEmpty(OPENSCAD_LIBRARIES_DIR) {
+ isEmpty(GLIB2_INCLUDEPATH) {
+ GLIB2_INCLUDEPATH_1 = $$OPENSCAD_LIBRARIES_DIR/include/glib-2.0
+ GLIB2_INCLUDEPATH_2 = $$OPENSCAD_LIBRARIES_DIR/lib/glib-2.0/include
+ GLIB2_LIBPATH = $$OPENSCAD_LIBRARIES_DIR/lib
+ }
+}
+
+isEmpty(GLIB2_INCLUDEPATH) {
+ GLIB2_CFLAGS = $$system("pkg-config --cflags glib-2.0")
+} else {
+ GLIB2_CFLAGS = -I$$GLIB2_INCLUDEPATH_1
+ GLIB2_CFLAGS += -I$$GLIB2_INCLUDEPATH_2
+}
+
+isEmpty(GLIB2_LIBPATH) {
+ GLIB2_LIBS = $$system("pkg-config --libs glib-2.0")
+} else {
+ GLIB2_LIBS = -L$$GLIB2_LIBPATH -lglib-2.0
+}
+
+QMAKE_CXXFLAGS += $$GLIB2_CFLAGS
+LIBS += $$GLIB2_LIBS
+}
diff --git a/openscad.pro b/openscad.pro
index b38419ef..ec5af20c 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -8,7 +8,7 @@
# OPENCSGDIR
# OPENSCAD_LIBRARIES
#
-# Please see the 'Buildling' sections of the OpenSCAD user manual
+# Please see the 'Building' sections of the OpenSCAD user manual
# for updated tips & workarounds.
#
# http://en.wikibooks.org/wiki/OpenSCAD_User_Manual
@@ -156,6 +156,7 @@ CONFIG += cgal
CONFIG += opencsg
CONFIG += boost
CONFIG += eigen
+CONFIG += glib-2.0
#Uncomment the following line to enable QCodeEdit
#CONFIG += qcodeedit
diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh
index b63c6770..e5871989 100755
--- a/scripts/check-dependencies.sh
+++ b/scripts/check-dependencies.sh
@@ -66,6 +66,21 @@ cgal_sysver()
cgal_sysver_result=`grep "define *CGAL_VERSION *[0-9.]*" $cgalpath | awk '{print $3}'`
}
+glib2_sysver()
+{
+ #Get architecture triplet - e.g. x86_64-linux-gnu
+ glib2archtriplet=`gcc -dumpmachine 2>/dev/null`
+ if [ -z "$VAR" ]; then
+ glib2archtriplet=`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`
+ fi
+ glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h
+ if [ ! -e $glib2path ]; then return; fi
+ glib2major=`grep "define *GLIB_MAJOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
+ glib2minor=`grep "define *GLIB_MINOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
+ glib2micro=`grep "define *GLIB_MICRO_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
+ glib2_sysver_result="${glib2major}.${glib2minor}.${glib2micro}"
+}
+
boost_sysver()
{
boostpath=$1/include/boost/version.hpp
@@ -530,7 +545,7 @@ checkargs()
main()
{
- deps="qt4 cgal gmp mpfr boost opencsg glew eigen gcc bison flex make"
+ deps="qt4 cgal gmp mpfr boost opencsg glew eigen glib2 gcc bison flex make"
#deps="$deps curl git" # not technically necessary for build
#deps="$deps python cmake imagemagick" # only needed for tests
#deps="cgal"
diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh
index e652c473..8d912c35 100755
--- a/scripts/uni-build-dependencies.sh
+++ b/scripts/uni-build-dependencies.sh
@@ -603,5 +603,6 @@ build_boost 1.53.0
build_cgal 4.0.2
build_glew 1.9.0
build_opencsg 1.3.2
+build_glib2 2.38.2
echo "OpenSCAD dependencies built and installed to " $BASEDIR
diff --git a/scripts/uni-get-dependencies.sh b/scripts/uni-get-dependencies.sh
index a0306ef8..d2408c00 100755
--- a/scripts/uni-get-dependencies.sh
+++ b/scripts/uni-get-dependencies.sh
@@ -8,7 +8,7 @@ get_fedora_deps()
{
sudo yum install qt-devel bison flex eigen3-devel python-paramiko \
boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \
- opencsg-devel git libXmu-devel curl imagemagick ImageMagick make \
+ opencsg-devel git libXmu-devel curl imagemagick ImageMagick glib2-devel make \
xorg-x11-server-Xvfb
}
@@ -21,7 +21,7 @@ get_altlinux_deps()
{
for i in boost-devel boost-filesystem-devel gcc4.5 gcc4.5-c++ boost-program_options-devel \
boost-thread-devel boost-system-devel boost-regex-devel eigen3 libmpfr libgmp libgmp_cxx-devel qt4-devel libcgal-devel git-core \
- libglew-devel flex bison curl imagemagick; do sudo apt-get install $i; done
+ libglew-devel flex bison curl imagemagick glib2-devel; do sudo apt-get install $i; done
}
get_freebsd_deps()
@@ -29,20 +29,21 @@ get_freebsd_deps()
pkg_add -r bison boost-libs cmake git bash eigen3 flex gmake gmp mpfr \
xorg libGLU libXmu libXi xorg-vfbserver glew \
qt4-corelib qt4-gui qt4-moc qt4-opengl qt4-qmake qt4-rcc qt4-uic \
- opencsg cgal curl imagemagick
+ opencsg cgal curl imagemagick glib2-devel
}
get_netbsd_deps()
{
sudo pkgin install bison boost cmake git bash eigen flex gmake gmp mpfr \
qt4 glew cgal opencsg modular-xorg python27 py27-paramiko curl \
- imagemagick ImageMagick
+ imagemagick ImageMagick glib2-devel
}
get_opensuse_deps()
{
sudo zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \
- libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl
+ libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl \
+ glib2-devel
}
get_mageia_deps()
@@ -50,7 +51,7 @@ get_mageia_deps()
sudo urpmi ctags
sudo urpmi task-c-devel task-c++-devel libqt4-devel libgmp-devel \
libmpfr-devel libboost-devel eigen3-devel libglew-devel bison flex \
- cmake imagemagick python curl git x11-server-xvfb
+ cmake imagemagick glib2-devel python curl git x11-server-xvfb
}
get_debian_deps()
@@ -59,7 +60,7 @@ get_debian_deps()
libxmu-dev cmake bison flex git-core libboost-all-dev \
libXi-dev libmpfr-dev libboost-dev libglew-dev \
libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev \
- python-paramiko curl imagemagick; do
+ python-paramiko curl imagemagick libglib2.0-dev; do
sudo apt-get -y install $pkg;
done
}
diff --git a/src/AboutDialog.html b/src/AboutDialog.html
index 99e7c3b2..65a54d71 100644
--- a/src/AboutDialog.html
+++ b/src/AboutDialog.html
@@ -64,6 +64,7 @@ Please visit this link for a copy of the license: C++, GCC, clang
python
Nullsoft installer
+GLib
diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc
index b02b822e..8b39f6df 100644
--- a/src/PlatformUtils.cc
+++ b/src/PlatformUtils.cc
@@ -1,6 +1,8 @@
#include "PlatformUtils.h"
#include "boosty.h"
+#include
+
bool PlatformUtils::createLibraryPath()
{
std::string path = PlatformUtils::libraryPath();
@@ -114,6 +116,7 @@ std::string PlatformUtils::info()
<< "\nOpenCSG version: " << OPENCSG_VERSION_STRING
<< "\nQt version: " << qtVersion
<< "\nMingW build: " << mingwstatus
+ << "\nGLib version: " << GLIB_MAJOR_VERSION << "." << GLIB_MINOR_VERSION << "." << GLIB_MICRO_VERSION
<< "\nOPENSCADPATH: " << getenv("OPENSCADPATH") << "\n"
;
return s.str();
diff --git a/src/func.cc b/src/func.cc
index 865a2b42..4587f725 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -45,6 +45,8 @@
#include
#include
+/*Unicode support for string lengths and array accesses*/
+#include
#ifdef __WIN32__
#include
@@ -306,7 +308,11 @@ Value builtin_length(const Context *, const EvalContext *evalctx)
{
if (evalctx->numArgs() == 1) {
if (evalctx->getArgValue(0).type() == Value::VECTOR) return Value(int(evalctx->getArgValue(0).toVector().size()));
- if (evalctx->getArgValue(0).type() == Value::STRING) return Value(int(evalctx->getArgValue(0).toString().size()));
+ if (evalctx->getArgValue(0).type() == Value::STRING) {
+ //Unicode glyph count for the length -- rather than the string (num. of bytes) length.
+ std::string text = evalctx->getArgValue(0).toString();
+ return Value(int( g_utf8_strlen( text.c_str(), text.size() ) ));
+ }
}
return Value();
}
@@ -380,10 +386,17 @@ Value builtin_lookup(const Context *, const EvalContext *evalctx)
num_returns_per_match : int;
index_col_num : int;
+ The search string and searched strings can be unicode strings.
Examples:
Index values return as list:
search("a","abcdabcd");
- - returns [0,4]
+ - returns [0]
+ search("Л","Л"); //A unicode string
+ - returns [0]
+ search("🂡aЛ","a🂡Л🂡a🂡Л🂡a",0);
+ - returns [[1,3,5,7],[0,4,8],[2,6]]
+ search("a","abcdabcd",0); //Search up to all matches
+ - returns [[0,4]]
search("a","abcdabcd",1);
- returns [0]
search("e","abcdabcd",1);
@@ -433,16 +446,25 @@ Value builtin_search(const Context *, const EvalContext *evalctx)
}
} else if (findThis.type() == Value::STRING) {
unsigned int searchTableSize;
- if (searchTable.type() == Value::STRING) searchTableSize = searchTable.toString().size();
- else searchTableSize = searchTable.toVector().size();
- for (size_t i = 0; i < findThis.toString().size(); i++) {
+ //Unicode glyph count for the length
+ unsigned int findThisSize = g_utf8_strlen( findThis.toString().c_str(), findThis.toString().size() );
+ if (searchTable.type() == Value::STRING) {
+ searchTableSize = g_utf8_strlen( searchTable.toString().c_str(), searchTable.toString().size() );
+ } else {
+ searchTableSize = searchTable.toVector().size();
+ }
+ for (size_t i = 0; i < findThisSize; i++) {
unsigned int matchCount = 0;
Value::VectorType resultvec;
for (size_t j = 0; j < searchTableSize; j++) {
- if ((searchTable.type() == Value::VECTOR &&
- findThis.toString()[i] == searchTable.toVector()[j].toVector()[index_col_num].toString()[0]) ||
- (searchTable.type() == Value::STRING &&
- findThis.toString()[i] == searchTable.toString()[j])) {
+ gchar* ptr_ft = g_utf8_offset_to_pointer(findThis.toString().c_str(), i);
+ gchar* ptr_st = NULL;
+ if(searchTable.type() == Value::VECTOR) {
+ ptr_st = g_utf8_offset_to_pointer(searchTable.toVector()[j].toVector()[index_col_num].toString().c_str(), 0);
+ } else if(searchTable.type() == Value::STRING){
+ ptr_st = g_utf8_offset_to_pointer(searchTable.toString().c_str(), j);
+ }
+ if( (ptr_ft) && (ptr_st) && (g_utf8_get_char(ptr_ft) == g_utf8_get_char(ptr_st)) ) {
Value resultValue((double(j)));
matchCount++;
if (num_returns_per_match == 1) {
@@ -454,7 +476,14 @@ Value builtin_search(const Context *, const EvalContext *evalctx)
if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) break;
}
}
- if (matchCount == 0) PRINTB(" WARNING: search term not found: \"%s\"", findThis.toString()[i]);
+ if (matchCount == 0) {
+ gchar* ptr_ft = g_utf8_offset_to_pointer(findThis.toString().c_str(), i);
+ gchar utf8_of_cp[6] = ""; //A buffer for a single unicode character to be copied into
+ if(ptr_ft) {
+ g_utf8_strncpy( utf8_of_cp, ptr_ft, 1 );
+ }
+ PRINTB(" WARNING: search term not found: \"%s\"", utf8_of_cp );
+ }
if (num_returns_per_match == 0 || num_returns_per_match > 1) {
returnvec.push_back(Value(resultvec));
}
diff --git a/src/value.cc b/src/value.cc
index 5afb650c..c8a88c69 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -36,6 +36,8 @@
#include
#include "boost-utils.h"
#include "boosty.h"
+/*Unicode support for string lengths and array accesses*/
+#include
std::ostream &operator<<(std::ostream &stream, const Filename &filename)
{
@@ -579,14 +581,28 @@ Value Value::operator-() const
}
*/
+/*
+ * bracket operation [] detecting multi-byte unicode.
+ * If the string is multi-byte unicode then the index will offset to the character (2 or 4 byte) and not to the byte.
+ * A 'normal' string with byte chars are a subset of unicode and still work.
+ */
class bracket_visitor : public boost::static_visitor
{
public:
Value operator()(const std::string &str, const double &idx) const {
int i = int(idx);
Value v;
+ //Check that the index is positive and less than the size in bytes
if ((i >= 0) && (i < (int)str.size())) {
- v = Value(str[int(idx)]);
+ //Ensure character (not byte) index is inside the character/glyph array
+ if( (unsigned) i < g_utf8_strlen( str.c_str(), str.size() ) ) {
+ gchar utf8_of_cp[6] = ""; //A buffer for a single unicode character to be copied into
+ gchar* ptr = g_utf8_offset_to_pointer(str.c_str(), i);
+ if(ptr) {
+ g_utf8_strncpy(utf8_of_cp, ptr, 1);
+ }
+ v = std::string(utf8_of_cp);
+ }
// std::cout << "bracket_visitor: " << v << "\n";
}
return v;
diff --git a/testdata/scad/misc/search-tests-unicode.scad b/testdata/scad/misc/search-tests-unicode.scad
new file mode 100644
index 00000000..d863eff9
--- /dev/null
+++ b/testdata/scad/misc/search-tests-unicode.scad
@@ -0,0 +1,116 @@
+//Test search with unicode strings
+
+//Helper function that pretty prints our search test
+//Expected result is checked against execution of a search() invocation and OK/FAIL is indicated
+module test_search_and_echo( exp_res, search_to_find, search_to_search, search_up_to_num_matches = undef)
+{
+ if(undef != search_up_to_num_matches)
+ {
+ assign( test_res = search(search_to_find, search_to_search, search_up_to_num_matches) )
+ echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ", ", search_up_to_num_matches, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" ));
+ }
+ else
+ {
+ assign( test_res = search(search_to_find, search_to_search) )
+ echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" ));
+ }
+}
+
+
+//"Normal" text for comparison
+echo ("----- Lookup of 1 byte into 1 byte");
+//Hits - up_to_count 1
+test_search_and_echo( [0], "a","aaaa" );
+test_search_and_echo( [0], "a","aaaa",1 );
+test_search_and_echo( [0,0], "aa","aaaa" );
+test_search_and_echo( [0,0], "aa","aaaa",1 );
+
+
+//Hits - up to count 1+ (incl 0 == all)
+test_search_and_echo( [[0,1,2,3]] , "a","aaaa",0 );
+test_search_and_echo( [[0,1]], "a","aaaa",2 );
+test_search_and_echo( [[0,1,2]], "a","aaaa",3 );
+test_search_and_echo( [[0,1,2,3]] , "a","aaaa",4 );
+test_search_and_echo( [[0,1,2,3],[0,1,2,3]] , "aa","aaaa",0 );
+//Misses
+test_search_and_echo( [], "b","aaaa" );
+test_search_and_echo( [], "b","aaaa",1 );
+test_search_and_echo( [[]], "b","aaaa",0 );
+test_search_and_echo( [[]], "b","aaaa",2 );
+
+test_search_and_echo( [], "bb","aaaa" );
+test_search_and_echo( [], "bb","aaaa",1 );
+test_search_and_echo( [[],[]], "bb","aaaa",0 );
+test_search_and_echo( [[],[]], "bb","aaaa",2 );
+//Miss - empties
+test_search_and_echo( [], "","aaaa" );
+test_search_and_echo( [], "","" );
+test_search_and_echo( [], "a","" );
+
+
+//Unicode tests
+echo ("----- Lookup of multi-byte into 1 byte");
+test_search_and_echo( [], "Л","aaaa" );
+test_search_and_echo( [], "🂡","aaaa" );
+test_search_and_echo( [[]], "Л","aaaa",0 );
+test_search_and_echo( [[]], "🂡","aaaa",0 );
+
+test_search_and_echo( [], "ЛЛ","aaaa" );
+test_search_and_echo( [], "🂡🂡","aaaa" );
+test_search_and_echo( [[],[]], "ЛЛ","aaaa",0 );
+test_search_and_echo( [[],[]], "🂡🂡","aaaa",0 );
+
+echo ("----- Lookup of 1-byte into multi-byte");
+test_search_and_echo( [] , "a","ЛЛЛЛ" );
+test_search_and_echo( [] , "a","🂡🂡🂡🂡" );
+test_search_and_echo( [] , "a","ЛЛЛЛ",1 );
+
+test_search_and_echo( [[]] , "a","🂡🂡🂡🂡",0 );
+test_search_and_echo( [[]] , "a","🂡🂡🂡🂡",2 );
+
+echo ("----- Lookup of 1-byte into mixed multi-byte");
+test_search_and_echo( [0], "a","aЛaЛaЛaЛa" );
+test_search_and_echo( [0], "a","a🂡a🂡a🂡a🂡a" );
+test_search_and_echo( [0], "a","a🂡Л🂡a🂡Л🂡a" );
+
+test_search_and_echo( [[0,2,4,6,8]], "a","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[0,2,4,6,8]], "a","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[0,4,8]] , "a","a🂡Л🂡a🂡Л🂡a", 0 );
+
+echo ("----- Lookup of 2-byte into 2-byte");
+test_search_and_echo( [0] , "Л","ЛЛЛЛ" );
+test_search_and_echo( [[0,1,2,3]] , "Л","ЛЛЛЛ",0 );
+
+echo ("----- Lookup of 2-byte into 4-byte");
+test_search_and_echo( [] , "Л","🂡🂡🂡🂡" );
+
+echo ("----- Lookup of 4-byte into 4-byte");
+test_search_and_echo( [0] , "🂡","🂡🂡🂡🂡" );
+test_search_and_echo( [[0,1,2,3]], "🂡","🂡🂡🂡🂡",0 );
+
+echo ("----- Lookup of 4-byte into 2-byte");
+test_search_and_echo( [] , "🂡","ЛЛЛЛ" );
+
+echo ("----- Lookup of 2-byte into mixed multi-byte");
+test_search_and_echo( [1] , "Л","aЛaЛaЛaЛa",1 );
+test_search_and_echo( [] , "Л","a🂡a🂡a🂡a🂡a", 1 );
+test_search_and_echo( [2] , "Л","a🂡Л🂡a🂡Л🂡a", 1 );
+
+test_search_and_echo( [[1,3,5,7]] , "Л","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[]] , "Л","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[2,6]] , "Л","a🂡Л🂡a🂡Л🂡a", 0 );
+
+echo ("----- Lookup of 4-byte into mixed multi-byte");
+test_search_and_echo( [] , "🂡","aЛaЛaЛaЛa",1 );
+test_search_and_echo( [1] , "🂡","a🂡a🂡a🂡a🂡a", 1 );
+
+test_search_and_echo( [[]] , "🂡","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[1,3,5,7]] , "🂡","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[1,3,5,7]] , "🂡","a🂡Л🂡a🂡Л🂡a", 0 );
+
+echo ("----- Lookup of mixed multi-byte into mixed multi-byte");
+test_search_and_echo( [[0,2,4,6,8],[1,3,5,7],[]], "aЛ🂡","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[0,2,4,6,8],[],[1,3,5,7]], "aЛ🂡","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[0,4,8],[2,6],[1,3,5,7]] , "aЛ🂡","a🂡Л🂡a🂡Л🂡a", 0 );
+test_search_and_echo( [[1,3,5,7],[0,4,8],[2,6]] , "🂡aЛ","a🂡Л🂡a🂡Л🂡a", 0 );
+
diff --git a/testdata/scad/misc/string-unicode.scad b/testdata/scad/misc/string-unicode.scad
new file mode 100644
index 00000000..d8e3e5c9
--- /dev/null
+++ b/testdata/scad/misc/string-unicode.scad
@@ -0,0 +1,36 @@
+//Test how well arrays of unicode string are accessed.
+
+texts_array = [
+"DEADBEEF",
+"Ленивый рыжий кот",
+"كسول الزنجبيل القط",
+"懶惰的姜貓",
+"äöü ÄÖÜ ß",
+"😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐",
+"⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏",
+"🂡🂱🃁🃑",
+];
+
+text_2bytes = "Ленивый рыжий кот";
+text_4bytes = "🂡🂱🃁🃑";
+
+
+//Test all the normal accesses
+for (text_array_idx = [0:(len(texts_array)-1)])
+{
+ echo( "[", text_array_idx, "] = ", texts_array[text_array_idx], " of len=", len(texts_array[text_array_idx]), ":" );
+ for (text_idx = [0:(len(texts_array[text_array_idx])-1)])
+ {
+ echo( " [", text_idx, ,"]=", texts_array[text_array_idx][text_idx] );
+ }
+}
+
+//Test one past the last element of (x-byte unicode). This will be one past the length but inside the char length of the string
+echo( "Past end of unicode only 2-byte ", text_2bytes[len(text_2bytes)] );
+echo( "Past end of unicode only 4-byte ", text_4bytes[len(text_4bytes)] );
+
+//Test past the last element of (x-byte unicode). Outside both lengths.
+echo( "Past end of both 2-byte ", text_2bytes[ len(text_2bytes) * 2 ] );
+echo( "Past end of both 4-byte ", text_4bytes[ len(text_4bytes) * 4 ] );
+
+
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4cc86f54..fd5097a8 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -587,8 +587,8 @@ set(OFFSCREEN_SOURCES
../src/OpenCSGRenderer.cc)
add_library(tests-core STATIC ${CORE_SOURCES})
-target_link_libraries(tests-core ${OPENGL_LIBRARIES})
-set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${Boost_LIBRARIES})
+target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} )
+set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} )
add_library(tests-common STATIC ${COMMON_SOURCES})
target_link_libraries(tests-common tests-core)
@@ -808,8 +808,10 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/dim-all.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-indexing.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-unicode.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/vector-values.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests-unicode.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad
diff --git a/tests/regression/echotest/search-tests-unicode-expected.echo b/tests/regression/echotest/search-tests-unicode-expected.echo
new file mode 100644
index 00000000..801bc8c8
--- /dev/null
+++ b/tests/regression/echotest/search-tests-unicode-expected.echo
@@ -0,0 +1,109 @@
+ECHO: "----- Lookup of 1 byte into 1 byte"
+ECHO: "Expect [0] for search(a, aaaa)=[0]. OK"
+ECHO: "Expect [0] for search(a, aaaa, 1)=[0]. OK"
+ECHO: "Expect [0, 0] for search(aa, aaaa)=[0, 0]. OK"
+ECHO: "Expect [0, 0] for search(aa, aaaa, 1)=[0, 0]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(a, aaaa, 0)=[[0, 1, 2, 3]]. OK"
+ECHO: "Expect [[0, 1]] for search(a, aaaa, 2)=[[0, 1]]. OK"
+ECHO: "Expect [[0, 1, 2]] for search(a, aaaa, 3)=[[0, 1, 2]]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(a, aaaa, 4)=[[0, 1, 2, 3]]. OK"
+ECHO: "Expect [[0, 1, 2, 3], [0, 1, 2, 3]] for search(aa, aaaa, 0)=[[0, 1, 2, 3], [0, 1, 2, 3]]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(b, aaaa)=[]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(b, aaaa, 1)=[]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[]] for search(b, aaaa, 0)=[[]]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[]] for search(b, aaaa, 2)=[[]]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(bb, aaaa)=[]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(bb, aaaa, 1)=[]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[], []] for search(bb, aaaa, 0)=[[], []]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[], []] for search(bb, aaaa, 2)=[[], []]. OK"
+ECHO: "Expect [] for search(, aaaa)=[]. OK"
+ECHO: "Expect [] for search(, )=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, )=[]. OK"
+ECHO: "----- Lookup of multi-byte into 1 byte"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(Л, aaaa)=[]. OK"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡, aaaa)=[]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[]] for search(Л, aaaa, 0)=[[]]. OK"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[]] for search(🂡, aaaa, 0)=[[]]. OK"
+ WARNING: search term not found: "Л"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(ЛЛ, aaaa)=[]. OK"
+ WARNING: search term not found: "🂡"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡🂡, aaaa)=[]. OK"
+ WARNING: search term not found: "Л"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[], []] for search(ЛЛ, aaaa, 0)=[[], []]. OK"
+ WARNING: search term not found: "🂡"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[], []] for search(🂡🂡, aaaa, 0)=[[], []]. OK"
+ECHO: "----- Lookup of 1-byte into multi-byte"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, ЛЛЛЛ)=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, 🂡🂡🂡🂡)=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, ЛЛЛЛ, 1)=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [[]] for search(a, 🂡🂡🂡🂡, 0)=[[]]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [[]] for search(a, 🂡🂡🂡🂡, 2)=[[]]. OK"
+ECHO: "----- Lookup of 1-byte into mixed multi-byte"
+ECHO: "Expect [0] for search(a, aЛaЛaЛaЛa)=[0]. OK"
+ECHO: "Expect [0] for search(a, a🂡a🂡a🂡a🂡a)=[0]. OK"
+ECHO: "Expect [0] for search(a, a🂡Л🂡a🂡Л🂡a)=[0]. OK"
+ECHO: "Expect [[0, 2, 4, 6, 8]] for search(a, aЛaЛaЛaЛa, 0)=[[0, 2, 4, 6, 8]]. OK"
+ECHO: "Expect [[0, 2, 4, 6, 8]] for search(a, a🂡a🂡a🂡a🂡a, 0)=[[0, 2, 4, 6, 8]]. OK"
+ECHO: "Expect [[0, 4, 8]] for search(a, a🂡Л🂡a🂡Л🂡a, 0)=[[0, 4, 8]]. OK"
+ECHO: "----- Lookup of 2-byte into 2-byte"
+ECHO: "Expect [0] for search(Л, ЛЛЛЛ)=[0]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(Л, ЛЛЛЛ, 0)=[[0, 1, 2, 3]]. OK"
+ECHO: "----- Lookup of 2-byte into 4-byte"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(Л, 🂡🂡🂡🂡)=[]. OK"
+ECHO: "----- Lookup of 4-byte into 4-byte"
+ECHO: "Expect [0] for search(🂡, 🂡🂡🂡🂡)=[0]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(🂡, 🂡🂡🂡🂡, 0)=[[0, 1, 2, 3]]. OK"
+ECHO: "----- Lookup of 4-byte into 2-byte"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡, ЛЛЛЛ)=[]. OK"
+ECHO: "----- Lookup of 2-byte into mixed multi-byte"
+ECHO: "Expect [1] for search(Л, aЛaЛaЛaЛa, 1)=[1]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(Л, a🂡a🂡a🂡a🂡a, 1)=[]. OK"
+ECHO: "Expect [2] for search(Л, a🂡Л🂡a🂡Л🂡a, 1)=[2]. OK"
+ECHO: "Expect [[1, 3, 5, 7]] for search(Л, aЛaЛaЛaЛa, 0)=[[1, 3, 5, 7]]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[]] for search(Л, a🂡a🂡a🂡a🂡a, 0)=[[]]. OK"
+ECHO: "Expect [[2, 6]] for search(Л, a🂡Л🂡a🂡Л🂡a, 0)=[[2, 6]]. OK"
+ECHO: "----- Lookup of 4-byte into mixed multi-byte"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡, aЛaЛaЛaЛa, 1)=[]. OK"
+ECHO: "Expect [1] for search(🂡, a🂡a🂡a🂡a🂡a, 1)=[1]. OK"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[]] for search(🂡, aЛaЛaЛaЛa, 0)=[[]]. OK"
+ECHO: "Expect [[1, 3, 5, 7]] for search(🂡, a🂡a🂡a🂡a🂡a, 0)=[[1, 3, 5, 7]]. OK"
+ECHO: "Expect [[1, 3, 5, 7]] for search(🂡, a🂡Л🂡a🂡Л🂡a, 0)=[[1, 3, 5, 7]]. OK"
+ECHO: "----- Lookup of mixed multi-byte into mixed multi-byte"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[0, 2, 4, 6, 8], [1, 3, 5, 7], []] for search(aЛ🂡, aЛaЛaЛaЛa, 0)=[[0, 2, 4, 6, 8], [1, 3, 5, 7], []]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[0, 2, 4, 6, 8], [], [1, 3, 5, 7]] for search(aЛ🂡, a🂡a🂡a🂡a🂡a, 0)=[[0, 2, 4, 6, 8], [], [1, 3, 5, 7]]. OK"
+ECHO: "Expect [[0, 4, 8], [2, 6], [1, 3, 5, 7]] for search(aЛ🂡, a🂡Л🂡a🂡Л🂡a, 0)=[[0, 4, 8], [2, 6], [1, 3, 5, 7]]. OK"
+ECHO: "Expect [[1, 3, 5, 7], [0, 4, 8], [2, 6]] for search(🂡aЛ, a🂡Л🂡a🂡Л🂡a, 0)=[[1, 3, 5, 7], [0, 4, 8], [2, 6]]. OK"
diff --git a/tests/regression/echotest/string-unicode-expected.echo b/tests/regression/echotest/string-unicode-expected.echo
new file mode 100644
index 00000000..b4b848fd
--- /dev/null
+++ b/tests/regression/echotest/string-unicode-expected.echo
@@ -0,0 +1,104 @@
+ECHO: "[", 0, "] = ", "DEADBEEF", " of len=", 8, ":"
+ECHO: " [", 0, "]=", "D"
+ECHO: " [", 1, "]=", "E"
+ECHO: " [", 2, "]=", "A"
+ECHO: " [", 3, "]=", "D"
+ECHO: " [", 4, "]=", "B"
+ECHO: " [", 5, "]=", "E"
+ECHO: " [", 6, "]=", "E"
+ECHO: " [", 7, "]=", "F"
+ECHO: "[", 1, "] = ", "Ленивый рыжий кот", " of len=", 17, ":"
+ECHO: " [", 0, "]=", "Л"
+ECHO: " [", 1, "]=", "е"
+ECHO: " [", 2, "]=", "н"
+ECHO: " [", 3, "]=", "и"
+ECHO: " [", 4, "]=", "в"
+ECHO: " [", 5, "]=", "ы"
+ECHO: " [", 6, "]=", "й"
+ECHO: " [", 7, "]=", " "
+ECHO: " [", 8, "]=", "р"
+ECHO: " [", 9, "]=", "ы"
+ECHO: " [", 10, "]=", "ж"
+ECHO: " [", 11, "]=", "и"
+ECHO: " [", 12, "]=", "й"
+ECHO: " [", 13, "]=", " "
+ECHO: " [", 14, "]=", "к"
+ECHO: " [", 15, "]=", "о"
+ECHO: " [", 16, "]=", "т"
+ECHO: "[", 2, "] = ", "كسول الزنجبيل القط", " of len=", 18, ":"
+ECHO: " [", 0, "]=", "ك"
+ECHO: " [", 1, "]=", "س"
+ECHO: " [", 2, "]=", "و"
+ECHO: " [", 3, "]=", "ل"
+ECHO: " [", 4, "]=", " "
+ECHO: " [", 5, "]=", "ا"
+ECHO: " [", 6, "]=", "ل"
+ECHO: " [", 7, "]=", "ز"
+ECHO: " [", 8, "]=", "ن"
+ECHO: " [", 9, "]=", "ج"
+ECHO: " [", 10, "]=", "ب"
+ECHO: " [", 11, "]=", "ي"
+ECHO: " [", 12, "]=", "ل"
+ECHO: " [", 13, "]=", " "
+ECHO: " [", 14, "]=", "ا"
+ECHO: " [", 15, "]=", "ل"
+ECHO: " [", 16, "]=", "ق"
+ECHO: " [", 17, "]=", "ط"
+ECHO: "[", 3, "] = ", "懶惰的姜貓", " of len=", 5, ":"
+ECHO: " [", 0, "]=", "懶"
+ECHO: " [", 1, "]=", "惰"
+ECHO: " [", 2, "]=", "的"
+ECHO: " [", 3, "]=", "姜"
+ECHO: " [", 4, "]=", "貓"
+ECHO: "[", 4, "] = ", "äöü ÄÖÜ ß", " of len=", 9, ":"
+ECHO: " [", 0, "]=", "ä"
+ECHO: " [", 1, "]=", "ö"
+ECHO: " [", 2, "]=", "ü"
+ECHO: " [", 3, "]=", " "
+ECHO: " [", 4, "]=", "Ä"
+ECHO: " [", 5, "]=", "Ö"
+ECHO: " [", 6, "]=", "Ü"
+ECHO: " [", 7, "]=", " "
+ECHO: " [", 8, "]=", "ß"
+ECHO: "[", 5, "] = ", "😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐", " of len=", 16, ":"
+ECHO: " [", 0, "]=", "😁"
+ECHO: " [", 1, "]=", "😂"
+ECHO: " [", 2, "]=", "😃"
+ECHO: " [", 3, "]=", "😄"
+ECHO: " [", 4, "]=", "😅"
+ECHO: " [", 5, "]=", "😆"
+ECHO: " [", 6, "]=", "😇"
+ECHO: " [", 7, "]=", "😈"
+ECHO: " [", 8, "]=", "😉"
+ECHO: " [", 9, "]=", "😊"
+ECHO: " [", 10, "]=", "😋"
+ECHO: " [", 11, "]=", "😌"
+ECHO: " [", 12, "]=", "😍"
+ECHO: " [", 13, "]=", "😎"
+ECHO: " [", 14, "]=", "😏"
+ECHO: " [", 15, "]=", "😐"
+ECHO: "[", 6, "] = ", "⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏", " of len=", 15, ":"
+ECHO: " [", 0, "]=", "⠁"
+ECHO: " [", 1, "]=", "⠂"
+ECHO: " [", 2, "]=", "⠃"
+ECHO: " [", 3, "]=", "⠄"
+ECHO: " [", 4, "]=", "⠅"
+ECHO: " [", 5, "]=", "⠆"
+ECHO: " [", 6, "]=", "⠇"
+ECHO: " [", 7, "]=", "⠈"
+ECHO: " [", 8, "]=", "⠉"
+ECHO: " [", 9, "]=", "⠊"
+ECHO: " [", 10, "]=", "⠋"
+ECHO: " [", 11, "]=", "⠌"
+ECHO: " [", 12, "]=", "⠍"
+ECHO: " [", 13, "]=", "⠎"
+ECHO: " [", 14, "]=", "⠏"
+ECHO: "[", 7, "] = ", "🂡🂱🃁🃑", " of len=", 4, ":"
+ECHO: " [", 0, "]=", "🂡"
+ECHO: " [", 1, "]=", "🂱"
+ECHO: " [", 2, "]=", "🃁"
+ECHO: " [", 3, "]=", "🃑"
+ECHO: "Past end of unicode only 2-byte ", undef
+ECHO: "Past end of unicode only 4-byte ", undef
+ECHO: "Past end of both 2-byte ", undef
+ECHO: "Past end of both 4-byte ", undef
From 301ef946f05a320768d4b8f974a95e2a04a5fc0d Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Thu, 5 Dec 2013 12:20:40 -0500
Subject: [PATCH 22/66] Qt4 patches to make it build on 10.9
---
patches/qt4/patch-libtiff.diff | 18 +
.../patch-src_corelib_global_qglobal.h.diff | 14 +
...ns_bearer_corewlan_qcorewlanengine.mm.diff | 1382 +++++++++++++++++
3 files changed, 1414 insertions(+)
create mode 100644 patches/qt4/patch-libtiff.diff
create mode 100644 patches/qt4/patch-src_corelib_global_qglobal.h.diff
create mode 100644 patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff
diff --git a/patches/qt4/patch-libtiff.diff b/patches/qt4/patch-libtiff.diff
new file mode 100644
index 00000000..5b7f9ec3
--- /dev/null
+++ b/patches/qt4/patch-libtiff.diff
@@ -0,0 +1,18 @@
+--- src/3rdparty/libtiff/libtiff/tif_config.h
++++ src/3rdparty/libtiff/libtiff/tif_config.h
+@@ -317,15 +317,6 @@
+ /* Define to empty if `const' does not conform to ANSI C. */
+ /* #undef const */
+
+-/* Define to `__inline__' or `__inline' if that's what the C compiler
+- calls it, or to nothing if 'inline' is not supported under any name. */
+-#ifndef Q_OS_SYMBIAN
+-#ifndef __cplusplus
+-#undef inline
+-#define inline
+-#endif
+-#endif
+-
+ /* Define to `long int' if does not define. */
+ /* #undef off_t */
+
diff --git a/patches/qt4/patch-src_corelib_global_qglobal.h.diff b/patches/qt4/patch-src_corelib_global_qglobal.h.diff
new file mode 100644
index 00000000..8c55c5a6
--- /dev/null
+++ b/patches/qt4/patch-src_corelib_global_qglobal.h.diff
@@ -0,0 +1,14 @@
+--- src/corelib/global/qglobal.h.orig 2013-06-07 07:16:52.000000000 +0200
++++ src/corelib/global/qglobal.h 2013-10-27 14:05:22.000000000 +0100
+@@ -327,7 +327,10 @@
+ # if !defined(MAC_OS_X_VERSION_10_8)
+ # define MAC_OS_X_VERSION_10_8 MAC_OS_X_VERSION_10_7 + 1
+ # endif
+-# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_8)
++# if !defined(MAC_OS_X_VERSION_10_9)
++# define MAC_OS_X_VERSION_10_9 MAC_OS_X_VERSION_10_8 + 1
++# endif
++# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9)
+ # warning "This version of Mac OS X is unsupported"
+ # endif
+ #endif
diff --git a/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff b/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff
new file mode 100644
index 00000000..61b2eef3
--- /dev/null
+++ b/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff
@@ -0,0 +1,1382 @@
+--- src/plugins/bearer/corewlan/qcorewlanengine.mm
++++ src/plugins/bearer/corewlan/qcorewlanengine.mm
+@@ -52,29 +52,17 @@
+ #include
+
+ #include
+-#include
+-#include
+-#include
+-#include
+-#include
+-
+-#include
+-#include
+-#include
+-#include
+-
+-#include
++
++extern "C" { // Otherwise it won't find CWKeychain* symbols at link time
++#import
++}
++
+ #include "private/qcore_mac_p.h"
+
+ #include
+ #include
+
+-inline QString qt_NSStringToQString(const NSString *nsstr)
+-{ return QCFString::toQString(reinterpret_cast(nsstr)); }
+-
+-inline NSString *qt_QStringToNSString(const QString &qstr)
+-{ return [const_cast(reinterpret_cast(QCFString::toCFStringRef(qstr))) autorelease]; }
+-
++#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+
+ @interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject
+ {
+@@ -86,6 +74,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr)
+ - (void)notificationHandler;//:(NSNotification *)notification;
+ - (void)remove;
+ - (void)setEngine:(QCoreWlanEngine *)coreEngine;
++- (QCoreWlanEngine *)engine;
+ - (void)dealloc;
+
+ @property (assign) QCoreWlanEngine* engine;
+@@ -93,7 +82,6 @@ inline NSString *qt_QStringToNSString(const QString &qstr)
+ @end
+
+ @implementation QT_MANGLE_NAMESPACE(QNSListener)
+-@synthesize engine;
+
+ - (id) init
+ {
+@@ -101,7 +89,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr)
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+ notificationCenter = [NSNotificationCenter defaultCenter];
+ currentInterface = [CWInterface interfaceWithName:nil];
+- [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:kCWPowerDidChangeNotification object:nil];
++ [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:CWPowerDidChangeNotification object:nil];
+ [locker unlock];
+ [autoreleasepool release];
+ return self;
+@@ -120,6 +108,11 @@ inline NSString *qt_QStringToNSString(const QString &qstr)
+ [locker unlock];
+ }
+
++-(QCoreWlanEngine *)engine
++{
++ return engine;
++}
++
+ -(void)remove
+ {
+ [locker lock];
+@@ -133,7 +126,7 @@ inline NSString *qt_QStringToNSString(const QString &qstr)
+ }
+ @end
+
+-QT_MANGLE_NAMESPACE(QNSListener) *listener = 0;
++static QT_MANGLE_NAMESPACE(QNSListener) *listener = 0;
+
+ QT_BEGIN_NAMESPACE
+
+@@ -170,36 +163,28 @@ void QScanThread::run()
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+ QStringList found;
+ mutex.lock();
+- CWInterface *currentInterface = [CWInterface interfaceWithName:qt_QStringToNSString(interfaceName)];
++ CWInterface *currentInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceName)];
+ mutex.unlock();
+
+- if([currentInterface power]) {
++ if (currentInterface.powerOn) {
+ NSError *err = nil;
+- NSDictionary *parametersDict = [NSDictionary dictionaryWithObjectsAndKeys:
+- [NSNumber numberWithBool:YES], kCWScanKeyMerge,
+- [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType,
+- [NSNumber numberWithInteger:100], kCWScanKeyRestTime, nil];
+
+- NSArray* apArray = [currentInterface scanForNetworksWithParameters:parametersDict error:&err];
+- CWNetwork *apNetwork;
++ NSSet* apSet = [currentInterface scanForNetworksWithName:nil error:&err];
+
+ if (!err) {
+-
+- for(uint row=0; row < [apArray count]; row++ ) {
+- apNetwork = [apArray objectAtIndex:row];
+-
+- const QString networkSsid = qt_NSStringToQString([apNetwork ssid]);
++ for (CWNetwork *apNetwork in apSet) {
++ const QString networkSsid = QCFString::toQString(CFStringRef([apNetwork ssid]));
+ const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid));
+ found.append(id);
+
+ QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined;
+ bool known = isKnownSsid(networkSsid);
+- if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) {
+- if( networkSsid == qt_NSStringToQString( [currentInterface ssid])) {
++ if (currentInterface.serviceActive) {
++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) {
+ state = QNetworkConfiguration::Active;
+ }
+ }
+- if(state == QNetworkConfiguration::Undefined) {
++ if (state == QNetworkConfiguration::Undefined) {
+ if(known) {
+ state = QNetworkConfiguration::Discovered;
+ } else {
+@@ -207,7 +192,7 @@ void QScanThread::run()
+ }
+ }
+ QNetworkConfiguration::Purpose purpose = QNetworkConfiguration::UnknownPurpose;
+- if([[apNetwork securityMode] intValue] == kCWSecurityModeOpen) {
++ if ([apNetwork supportsSecurity:kCWSecurityNone]) {
+ purpose = QNetworkConfiguration::PublicPurpose;
+ } else {
+ purpose = QNetworkConfiguration::PrivatePurpose;
+@@ -237,8 +222,8 @@ void QScanThread::run()
+ interfaceName = ij.value();
+ }
+
+- if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) {
+- if( networkSsid == qt_NSStringToQString([currentInterface ssid])) {
++ if (currentInterface.serviceActive) {
++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) {
+ state = QNetworkConfiguration::Active;
+ }
+ }
+@@ -300,14 +285,14 @@ void QScanThread::getUserConfigurations()
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+ userProfiles.clear();
+
+- NSArray *wifiInterfaces = [CWInterface supportedInterfaces];
+- for(uint row=0; row < [wifiInterfaces count]; row++ ) {
++ NSSet *wifiInterfaces = [CWInterface interfaceNames];
++ for (NSString *ifName in wifiInterfaces) {
+
+- CWInterface *wifiInterface = [CWInterface interfaceWithName: [wifiInterfaces objectAtIndex:row]];
+- if ( ![wifiInterface power] )
++ CWInterface *wifiInterface = [CWInterface interfaceWithName: ifName];
++ if (!wifiInterface.powerOn)
+ continue;
+
+- NSString *nsInterfaceName = [wifiInterface name];
++ NSString *nsInterfaceName = wifiInterface.ssid;
+ // add user configured system networks
+ SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, (CFStringRef)@"Qt corewlan", nil, nil);
+ NSDictionary * airportPlist = (NSDictionary *)SCDynamicStoreCopyValue(dynRef, (CFStringRef)[NSString stringWithFormat:@"Setup:/Network/Interface/%@/AirPort", nsInterfaceName]);
+@@ -316,11 +301,11 @@ void QScanThread::getUserConfigurations()
+ NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"];
+
+ NSArray *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"];
+- for(NSString *ssidkey in thisSsidarray) {
+- QString thisSsid = qt_NSStringToQString(ssidkey);
++ for (NSString *ssidkey in thisSsidarray) {
++ QString thisSsid = QCFString::toQString(CFStringRef(ssidkey));
+ if(!userProfiles.contains(thisSsid)) {
+ QMap map;
+- map.insert(thisSsid, qt_NSStringToQString(nsInterfaceName));
++ map.insert(thisSsid, QCFString::toQString(CFStringRef(nsInterfaceName)));
+ userProfiles.insert(thisSsid, map);
+ }
+ }
+@@ -329,7 +314,7 @@ void QScanThread::getUserConfigurations()
+
+ // 802.1X user profiles
+ QString userProfilePath = QDir::homePath() + "/Library/Preferences/com.apple.eap.profiles.plist";
+- NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile:qt_QStringToNSString(userProfilePath)] autorelease];
++ NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile: (NSString *)QCFString::toCFStringRef(userProfilePath)] autorelease];
+ if(eapDict != nil) {
+ NSString *profileStr= @"Profiles";
+ NSString *nameStr = @"UserDefinedName";
+@@ -348,15 +333,15 @@ void QScanThread::getUserConfigurations()
+ QString ssid;
+ for(int i = 0; i < dictSize; i++) {
+ if([nameStr isEqualToString:keys[i]]) {
+- networkName = qt_NSStringToQString(objects[i]);
++ networkName = QCFString::toQString(CFStringRef(objects[i]));
+ }
+ if([networkSsidStr isEqualToString:keys[i]]) {
+- ssid = qt_NSStringToQString(objects[i]);
++ ssid = QCFString::toQString(CFStringRef(objects[i]));
+ }
+ if(!userProfiles.contains(networkName)
+ && !ssid.isEmpty()) {
+ QMap map;
+- map.insert(ssid, qt_NSStringToQString(nsInterfaceName));
++ map.insert(ssid, QCFString::toQString(CFStringRef(nsInterfaceName)));
+ userProfiles.insert(networkName, map);
+ }
+ }
+@@ -444,7 +429,7 @@ void QCoreWlanEngine::initialize()
+ QMutexLocker locker(&mutex);
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+
+- if([[CWInterface supportedInterfaces] count] > 0 && !listener) {
++ if ([[CWInterface interfaceNames] count] > 0 && !listener) {
+ listener = [[QT_MANGLE_NAMESPACE(QNSListener) alloc] init];
+ listener.engine = this;
+ hasWifi = true;
+@@ -479,141 +464,68 @@ void QCoreWlanEngine::connectToId(const QString &id)
+ QString interfaceString = getInterfaceFromId(id);
+
+ CWInterface *wifiInterface =
+- [CWInterface interfaceWithName: qt_QStringToNSString(interfaceString)];
++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)];
+
+- if ([wifiInterface power]) {
++ if (wifiInterface.powerOn) {
+ NSError *err = nil;
+- NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
+-
+ QString wantedSsid;
+-
+ QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
+
+ const QString idHash = QString::number(qHash(QLatin1String("corewlan:") + ptr->name));
+ const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name)));
+
+- bool using8021X = false;
+- if (idHash2 != id) {
+- NSArray *array = [CW8021XProfile allUser8021XProfiles];
+-
+- for (NSUInteger i = 0; i < [array count]; ++i) {
+- const QString networkNameHashCheck = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString([[array objectAtIndex:i] userDefinedName])));
+-
+- const QString ssidHash = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString([[array objectAtIndex:i] ssid])));
+-
+- if (id == networkNameHashCheck || id == ssidHash) {
+- const QString thisName = scanThread->getSsidFromNetworkName(id);
+- if (thisName.isEmpty())
+- wantedSsid = id;
+- else
+- wantedSsid = thisName;
+-
+- [params setValue: [array objectAtIndex:i] forKey:kCWAssocKey8021XProfile];
+- using8021X = true;
+- break;
+- }
++ QString wantedNetwork;
++ QMapIterator > i(scanThread->userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ wantedNetwork = i.key();
++ const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork));
++ if (id == networkNameHash) {
++ wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork);
++ break;
+ }
+ }
+
+- if (!using8021X) {
+- QString wantedNetwork;
+- QMapIterator > i(scanThread->userProfiles);
+- while (i.hasNext()) {
+- i.next();
+- wantedNetwork = i.key();
+- const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork));
+- if (id == networkNameHash) {
+- wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork);
+- break;
+- }
+- }
+- }
+- NSDictionary *scanParameters = [NSDictionary dictionaryWithObjectsAndKeys:
+- [NSNumber numberWithBool:YES], kCWScanKeyMerge,
+- [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType,
+- [NSNumber numberWithInteger:100], kCWScanKeyRestTime,
+- qt_QStringToNSString(wantedSsid), kCWScanKeySSID,
+- nil];
+-
+- NSArray *scanArray = [wifiInterface scanForNetworksWithParameters:scanParameters error:&err];
++ NSSet *scanSet = [wifiInterface scanForNetworksWithName:(NSString *)QCFString::toCFStringRef(wantedSsid) error:&err];
+
+ if(!err) {
+- for(uint row=0; row < [scanArray count]; row++ ) {
+- CWNetwork *apNetwork = [scanArray objectAtIndex:row];
+-
+- if(wantedSsid == qt_NSStringToQString([apNetwork ssid])) {
+-
+- if(!using8021X) {
+- SecKeychainAttribute attributes[3];
+-
+- NSString *account = [apNetwork ssid];
+- NSString *keyKind = @"AirPort network password";
+- NSString *keyName = account;
+-
+- attributes[0].tag = kSecAccountItemAttr;
+- attributes[0].data = (void *)[account UTF8String];
+- attributes[0].length = [account length];
+-
+- attributes[1].tag = kSecDescriptionItemAttr;
+- attributes[1].data = (void *)[keyKind UTF8String];
+- attributes[1].length = [keyKind length];
+-
+- attributes[2].tag = kSecLabelItemAttr;
+- attributes[2].data = (void *)[keyName UTF8String];
+- attributes[2].length = [keyName length];
+-
+- SecKeychainAttributeList attributeList = {3,attributes};
+-
+- SecKeychainSearchRef searchRef;
+- SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributeList, &searchRef);
+-
+- NSString *password = @"";
+- SecKeychainItemRef searchItem;
+-
+- if (SecKeychainSearchCopyNext(searchRef, &searchItem) == noErr) {
+- UInt32 realPasswordLength;
+- SecKeychainAttribute attributesW[8];
+- attributesW[0].tag = kSecAccountItemAttr;
+- SecKeychainAttributeList listW = {1,attributesW};
+- char *realPassword;
+- OSStatus status = SecKeychainItemCopyContent(searchItem, NULL, &listW, &realPasswordLength,(void **)&realPassword);
+-
+- if (status == noErr) {
+- if (realPassword != NULL) {
+-
+- QByteArray pBuf;
+- pBuf.resize(realPasswordLength);
+- pBuf.prepend(realPassword);
+- pBuf.insert(realPasswordLength,'\0');
+-
+- password = [NSString stringWithUTF8String:pBuf];
+- }
+- SecKeychainItemFreeContent(&listW, realPassword);
+- }
+-
+- CFRelease(searchItem);
+- } else {
+- qDebug() << "SecKeychainSearchCopyNext error";
+- }
+- [params setValue: password forKey: kCWAssocKeyPassphrase];
+- } // end using8021X
+-
+-
+- bool result = [wifiInterface associateToNetwork: apNetwork parameters:[NSDictionary dictionaryWithDictionary:params] error:&err];
++ for (CWNetwork *apNetwork in scanSet) {
++ CFDataRef ssidData = (CFDataRef)[apNetwork ssidData];
++ bool result = false;
++
++ SecIdentityRef identity = 0;
++ // Check first whether we require IEEE 802.1X authentication for the wanted SSID
++ if (CWKeychainCopyEAPIdentity(ssidData, &identity) == errSecSuccess) {
++ CFStringRef username = 0;
++ CFStringRef password = 0;
++ if (CWKeychainCopyEAPUsernameAndPassword(ssidData, &username, &password) == errSecSuccess) {
++ result = [wifiInterface associateToEnterpriseNetwork:apNetwork
++ identity:identity username:(NSString *)username password:(NSString *)password
++ error:&err];
++ CFRelease(username);
++ CFRelease(password);
++ }
++ CFRelease(identity);
++ } else {
++ CFStringRef password = 0;
++ if (CWKeychainCopyPassword(ssidData, &password) == errSecSuccess) {
++ result = [wifiInterface associateToNetwork:apNetwork password:(NSString *)password error:&err];
++ CFRelease(password);
++ }
++ }
+
+- if(!err) {
+- if(!result) {
+- emit connectionError(id, ConnectError);
+- } else {
+- return;
+- }
++ if (!err) {
++ if (!result) {
++ emit connectionError(id, ConnectError);
+ } else {
+- qDebug() <<"associate ERROR"<< qt_NSStringToQString([err localizedDescription ]);
++ return;
+ }
++ } else {
++ qDebug() <<"associate ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ]));
+ }
+ } //end scan network
+ } else {
+- qDebug() <<"scan ERROR"<< qt_NSStringToQString([err localizedDescription ]);
++ qDebug() <<"scan ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ]));
+ }
+ emit connectionError(id, InterfaceLookupError);
+ }
+@@ -631,10 +543,10 @@ void QCoreWlanEngine::disconnectFromId(const QString &id)
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+
+ CWInterface *wifiInterface =
+- [CWInterface interfaceWithName: qt_QStringToNSString(interfaceString)];
++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)];
+
+ [wifiInterface disassociate];
+- if ([[wifiInterface interfaceState]intValue] != kCWInterfaceStateInactive) {
++ if (wifiInterface.serviceActive) {
+ locker.unlock();
+ emit connectionError(id, DisconnectionError);
+ locker.relock();
+@@ -654,9 +566,9 @@ void QCoreWlanEngine::doRequestUpdate()
+
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+
+- NSArray *wifiInterfaces = [CWInterface supportedInterfaces];
+- for (uint row = 0; row < [wifiInterfaces count]; ++row) {
+- scanThread->interfaceName = qt_NSStringToQString([wifiInterfaces objectAtIndex:row]);
++ NSSet *wifiInterfaces = [CWInterface interfaceNames];
++ for (NSString *ifName in wifiInterfaces) {
++ scanThread->interfaceName = QCFString::toQString(CFStringRef(ifName));
+ scanThread->start();
+ }
+ locker.unlock();
+@@ -669,8 +581,8 @@ bool QCoreWlanEngine::isWifiReady(const QString &wifiDeviceName)
+ bool haswifi = false;
+ if(hasWifi) {
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+- CWInterface *defaultInterface = [CWInterface interfaceWithName: qt_QStringToNSString(wifiDeviceName)];
+- if([defaultInterface power]) {
++ CWInterface *defaultInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(wifiDeviceName)];
++ if (defaultInterface.powerOn) {
+ haswifi = true;
+ }
+ [autoreleasepool release];
+@@ -898,7 +810,7 @@ quint64 QCoreWlanEngine::startTime(const QString &identifier)
+ bool ok = false;
+ for(int i = 0; i < dictSize; i++) {
+ if([ssidStr isEqualToString:keys[i]]) {
+- const QString ident = QString::number(qHash(QLatin1String("corewlan:") + qt_NSStringToQString(objects[i])));
++ const QString ident = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef(objects[i]))));
+ if(ident == identifier) {
+ ok = true;
+ }
+@@ -944,3 +856,7 @@ quint64 QCoreWlanEngine::getBytes(const QString &interfaceName, bool b)
+ }
+
+ QT_END_NAMESPACE
++
++#else // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE
++#include "qcorewlanengine_10_6.mm"
++#endif
+diff --git a/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm b/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm
+new file mode 100644
+index 0000000..a3bf615
+--- /dev/null
++++ src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm
+@@ -0,0 +1,916 @@
++/****************************************************************************
++**
++** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
++** Contact: http://www.qt-project.org/legal
++**
++** This file is part of the plugins of the Qt Toolkit.
++**
++** $QT_BEGIN_LICENSE:LGPL$
++** Commercial License Usage
++** Licensees holding valid commercial Qt licenses may use this file in
++** accordance with the commercial license agreement provided with the
++** Software or, alternatively, in accordance with the terms contained in
++** a written agreement between you and Digia. For licensing terms and
++** conditions see http://qt.digia.com/licensing. For further information
++** use the contact form at http://qt.digia.com/contact-us.
++**
++** GNU Lesser General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU Lesser
++** General Public License version 2.1 as published by the Free Software
++** Foundation and appearing in the file LICENSE.LGPL included in the
++** packaging of this file. Please review the following information to
++** ensure the GNU Lesser General Public License version 2.1 requirements
++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
++**
++** In addition, as a special exception, Digia gives you certain additional
++** rights. These rights are described in the Digia Qt LGPL Exception
++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
++**
++** GNU General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU
++** General Public License version 3.0 as published by the Free Software
++** Foundation and appearing in the file LICENSE.GPL included in the
++** packaging of this file. Please review the following information to
++** ensure the GNU General Public License version 3.0 requirements will be
++** met: http://www.gnu.org/copyleft/gpl.html.
++**
++**
++** $QT_END_LICENSE$
++**
++****************************************************************************/
++
++#include
++
++@interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject
++{
++ NSNotificationCenter *notificationCenter;
++ CWInterface *currentInterface;
++ QCoreWlanEngine *engine;
++ NSLock *locker;
++}
++- (void)notificationHandler;//:(NSNotification *)notification;
++- (void)remove;
++- (void)setEngine:(QCoreWlanEngine *)coreEngine;
++- (QCoreWlanEngine *)engine;
++- (void)dealloc;
++
++@property (assign) QCoreWlanEngine* engine;
++
++@end
++
++@implementation QT_MANGLE_NAMESPACE(QNSListener)
++
++- (id) init
++{
++ [locker lock];
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++ notificationCenter = [NSNotificationCenter defaultCenter];
++ currentInterface = [CWInterface interfaceWithName:nil];
++ [notificationCenter addObserver:self selector:@selector(notificationHandler:) name:kCWPowerDidChangeNotification object:nil];
++ [locker unlock];
++ [autoreleasepool release];
++ return self;
++}
++
++-(void)dealloc
++{
++ [super dealloc];
++}
++
++-(void)setEngine:(QCoreWlanEngine *)coreEngine
++{
++ [locker lock];
++ if(!engine)
++ engine = coreEngine;
++ [locker unlock];
++}
++
++-(QCoreWlanEngine *)engine
++{
++ return engine;
++}
++
++-(void)remove
++{
++ [locker lock];
++ [notificationCenter removeObserver:self];
++ [locker unlock];
++}
++
++- (void)notificationHandler//:(NSNotification *)notification
++{
++ engine->requestUpdate();
++}
++@end
++
++static QT_MANGLE_NAMESPACE(QNSListener) *listener = 0;
++
++QT_BEGIN_NAMESPACE
++
++void networkChangeCallback(SCDynamicStoreRef/* store*/, CFArrayRef changedKeys, void *info)
++{
++ for ( long i = 0; i < CFArrayGetCount(changedKeys); i++) {
++
++ QString changed = QCFString::toQString(CFStringRef((CFStringRef)CFArrayGetValueAtIndex(changedKeys, i)));
++ if( changed.contains("/Network/Global/IPv4")) {
++ QCoreWlanEngine* wlanEngine = static_cast(info);
++ wlanEngine->requestUpdate();
++ }
++ }
++ return;
++}
++
++
++QScanThread::QScanThread(QObject *parent)
++ :QThread(parent)
++{
++}
++
++QScanThread::~QScanThread()
++{
++}
++
++void QScanThread::quit()
++{
++ wait();
++}
++
++void QScanThread::run()
++{
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++ QStringList found;
++ mutex.lock();
++ CWInterface *currentInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceName)];
++ mutex.unlock();
++
++ if([currentInterface power]) {
++ NSError *err = nil;
++ NSDictionary *parametersDict = [NSDictionary dictionaryWithObjectsAndKeys:
++ [NSNumber numberWithBool:YES], kCWScanKeyMerge,
++ [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType,
++ [NSNumber numberWithInteger:100], kCWScanKeyRestTime, nil];
++
++ NSArray* apArray = [currentInterface scanForNetworksWithParameters:parametersDict error:&err];
++ CWNetwork *apNetwork;
++
++ if (!err) {
++
++ for(uint row=0; row < [apArray count]; row++ ) {
++ apNetwork = [apArray objectAtIndex:row];
++
++ const QString networkSsid = QCFString::toQString(CFStringRef([apNetwork ssid]));
++ const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkSsid));
++ found.append(id);
++
++ QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined;
++ bool known = isKnownSsid(networkSsid);
++ if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) {
++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) {
++ state = QNetworkConfiguration::Active;
++ }
++ }
++ if(state == QNetworkConfiguration::Undefined) {
++ if(known) {
++ state = QNetworkConfiguration::Discovered;
++ } else {
++ state = QNetworkConfiguration::Undefined;
++ }
++ }
++ QNetworkConfiguration::Purpose purpose = QNetworkConfiguration::UnknownPurpose;
++ if([[apNetwork securityMode] intValue] == kCWSecurityModeOpen) {
++ purpose = QNetworkConfiguration::PublicPurpose;
++ } else {
++ purpose = QNetworkConfiguration::PrivatePurpose;
++ }
++
++ found.append(foundNetwork(id, networkSsid, state, interfaceName, purpose));
++
++ }
++ }
++ }
++ // add known configurations that are not around.
++ QMapIterator > i(userProfiles);
++ while (i.hasNext()) {
++ i.next();
++
++ QString networkName = i.key();
++ const QString id = QString::number(qHash(QLatin1String("corewlan:") + networkName));
++
++ if(!found.contains(id)) {
++ QString networkSsid = getSsidFromNetworkName(networkName);
++ const QString ssidId = QString::number(qHash(QLatin1String("corewlan:") + networkSsid));
++ QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined;
++ QString interfaceName;
++ QMapIterator ij(i.value());
++ while (ij.hasNext()) {
++ ij.next();
++ interfaceName = ij.value();
++ }
++
++ if( [currentInterface.interfaceState intValue] == kCWInterfaceStateRunning) {
++ if( networkSsid == QCFString::toQString(CFStringRef([currentInterface ssid]))) {
++ state = QNetworkConfiguration::Active;
++ }
++ }
++ if(state == QNetworkConfiguration::Undefined) {
++ if( userProfiles.contains(networkName)
++ && found.contains(ssidId)) {
++ state = QNetworkConfiguration::Discovered;
++ }
++ }
++
++ if(state == QNetworkConfiguration::Undefined) {
++ state = QNetworkConfiguration::Defined;
++ }
++
++ found.append(foundNetwork(id, networkName, state, interfaceName, QNetworkConfiguration::UnknownPurpose));
++ }
++ }
++ emit networksChanged();
++ [autoreleasepool release];
++}
++
++QStringList QScanThread::foundNetwork(const QString &id, const QString &name, const QNetworkConfiguration::StateFlags state, const QString &interfaceName, const QNetworkConfiguration::Purpose purpose)
++{
++ QStringList found;
++ QMutexLocker locker(&mutex);
++ QNetworkConfigurationPrivate *ptr = new QNetworkConfigurationPrivate;
++
++ ptr->name = name;
++ ptr->isValid = true;
++ ptr->id = id;
++ ptr->state = state;
++ ptr->type = QNetworkConfiguration::InternetAccessPoint;
++ ptr->bearerType = QNetworkConfiguration::BearerWLAN;
++ ptr->purpose = purpose;
++
++ fetchedConfigurations.append( ptr);
++ configurationInterface.insert(ptr->id, interfaceName);
++
++ locker.unlock();
++ locker.relock();
++ found.append(id);
++ return found;
++}
++
++QList QScanThread::getConfigurations()
++{
++ QMutexLocker locker(&mutex);
++
++ QList foundConfigurations = fetchedConfigurations;
++ fetchedConfigurations.clear();
++
++ return foundConfigurations;
++}
++
++void QScanThread::getUserConfigurations()
++{
++ QMutexLocker locker(&mutex);
++
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++ userProfiles.clear();
++
++ NSArray *wifiInterfaces = [CWInterface supportedInterfaces];
++ for(uint row=0; row < [wifiInterfaces count]; row++ ) {
++
++ CWInterface *wifiInterface = [CWInterface interfaceWithName: [wifiInterfaces objectAtIndex:row]];
++ if ( ![wifiInterface power] )
++ continue;
++
++ NSString *nsInterfaceName = [wifiInterface name];
++// add user configured system networks
++ SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, (CFStringRef)@"Qt corewlan", nil, nil);
++ NSDictionary * airportPlist = (NSDictionary *)SCDynamicStoreCopyValue(dynRef, (CFStringRef)[NSString stringWithFormat:@"Setup:/Network/Interface/%@/AirPort", nsInterfaceName]);
++ CFRelease(dynRef);
++ if(airportPlist != nil) {
++ NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"];
++
++ NSArray *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"];
++ for(NSString *ssidkey in thisSsidarray) {
++ QString thisSsid = QCFString::toQString(CFStringRef(ssidkey));
++ if(!userProfiles.contains(thisSsid)) {
++ QMap map;
++ map.insert(thisSsid, QCFString::toQString(CFStringRef(nsInterfaceName)));
++ userProfiles.insert(thisSsid, map);
++ }
++ }
++ CFRelease(airportPlist);
++ }
++
++ // 802.1X user profiles
++ QString userProfilePath = QDir::homePath() + "/Library/Preferences/com.apple.eap.profiles.plist";
++ NSDictionary* eapDict = [[[NSDictionary alloc] initWithContentsOfFile: (NSString *)QCFString::toCFStringRef(userProfilePath)] autorelease];
++ if(eapDict != nil) {
++ NSString *profileStr= @"Profiles";
++ NSString *nameStr = @"UserDefinedName";
++ NSString *networkSsidStr = @"Wireless Network";
++ for (id profileKey in eapDict) {
++ if ([profileStr isEqualToString:profileKey]) {
++ NSDictionary *itemDict = [eapDict objectForKey:profileKey];
++ for (id itemKey in itemDict) {
++
++ NSInteger dictSize = [itemKey count];
++ id objects[dictSize];
++ id keys[dictSize];
++
++ [itemKey getObjects:objects andKeys:keys];
++ QString networkName;
++ QString ssid;
++ for(int i = 0; i < dictSize; i++) {
++ if([nameStr isEqualToString:keys[i]]) {
++ networkName = QCFString::toQString(CFStringRef(objects[i]));
++ }
++ if([networkSsidStr isEqualToString:keys[i]]) {
++ ssid = QCFString::toQString(CFStringRef(objects[i]));
++ }
++ if(!userProfiles.contains(networkName)
++ && !ssid.isEmpty()) {
++ QMap map;
++ map.insert(ssid, QCFString::toQString(CFStringRef(nsInterfaceName)));
++ userProfiles.insert(networkName, map);
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ [autoreleasepool release];
++}
++
++QString QScanThread::getSsidFromNetworkName(const QString &name)
++{
++ QMutexLocker locker(&mutex);
++
++ QMapIterator > i(userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ QMap map = i.value();
++ QMapIterator ij(i.value());
++ while (ij.hasNext()) {
++ ij.next();
++ const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") +i.key()));
++ if(name == i.key() || name == networkNameHash) {
++ return ij.key();
++ }
++ }
++ }
++ return QString();
++}
++
++QString QScanThread::getNetworkNameFromSsid(const QString &ssid)
++{
++ QMutexLocker locker(&mutex);
++
++ QMapIterator > i(userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ QMap map = i.value();
++ QMapIterator ij(i.value());
++ while (ij.hasNext()) {
++ ij.next();
++ if(ij.key() == ssid) {
++ return i.key();
++ }
++ }
++ }
++ return QString();
++}
++
++bool QScanThread::isKnownSsid(const QString &ssid)
++{
++ QMutexLocker locker(&mutex);
++
++ QMapIterator > i(userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ QMap map = i.value();
++ if(map.keys().contains(ssid)) {
++ return true;
++ }
++ }
++ return false;
++}
++
++
++QCoreWlanEngine::QCoreWlanEngine(QObject *parent)
++: QBearerEngineImpl(parent), scanThread(0)
++{
++ scanThread = new QScanThread(this);
++ connect(scanThread, SIGNAL(networksChanged()),
++ this, SLOT(networksChanged()));
++}
++
++QCoreWlanEngine::~QCoreWlanEngine()
++{
++ while (!foundConfigurations.isEmpty())
++ delete foundConfigurations.takeFirst();
++ [listener remove];
++ [listener release];
++}
++
++void QCoreWlanEngine::initialize()
++{
++ QMutexLocker locker(&mutex);
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++
++ if([[CWInterface supportedInterfaces] count] > 0 && !listener) {
++ listener = [[QT_MANGLE_NAMESPACE(QNSListener) alloc] init];
++ listener.engine = this;
++ hasWifi = true;
++ } else {
++ hasWifi = false;
++ }
++ storeSession = NULL;
++
++ startNetworkChangeLoop();
++ [autoreleasepool release];
++}
++
++
++QString QCoreWlanEngine::getInterfaceFromId(const QString &id)
++{
++ QMutexLocker locker(&mutex);
++
++ return scanThread->configurationInterface.value(id);
++}
++
++bool QCoreWlanEngine::hasIdentifier(const QString &id)
++{
++ QMutexLocker locker(&mutex);
++
++ return scanThread->configurationInterface.contains(id);
++}
++
++void QCoreWlanEngine::connectToId(const QString &id)
++{
++ QMutexLocker locker(&mutex);
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++ QString interfaceString = getInterfaceFromId(id);
++
++ CWInterface *wifiInterface =
++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)];
++
++ if ([wifiInterface power]) {
++ NSError *err = nil;
++ NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
++
++ QString wantedSsid;
++
++ QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
++
++ const QString idHash = QString::number(qHash(QLatin1String("corewlan:") + ptr->name));
++ const QString idHash2 = QString::number(qHash(QLatin1String("corewlan:") + scanThread->getNetworkNameFromSsid(ptr->name)));
++
++ bool using8021X = false;
++ if (idHash2 != id) {
++ NSArray *array = [CW8021XProfile allUser8021XProfiles];
++
++ for (NSUInteger i = 0; i < [array count]; ++i) {
++ const QString networkNameHashCheck = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef([[array objectAtIndex:i] userDefinedName]))));
++
++ const QString ssidHash = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef([[array objectAtIndex:i] ssid]))));
++
++ if (id == networkNameHashCheck || id == ssidHash) {
++ const QString thisName = scanThread->getSsidFromNetworkName(id);
++ if (thisName.isEmpty())
++ wantedSsid = id;
++ else
++ wantedSsid = thisName;
++
++ [params setValue: [array objectAtIndex:i] forKey:kCWAssocKey8021XProfile];
++ using8021X = true;
++ break;
++ }
++ }
++ }
++
++ if (!using8021X) {
++ QString wantedNetwork;
++ QMapIterator > i(scanThread->userProfiles);
++ while (i.hasNext()) {
++ i.next();
++ wantedNetwork = i.key();
++ const QString networkNameHash = QString::number(qHash(QLatin1String("corewlan:") + wantedNetwork));
++ if (id == networkNameHash) {
++ wantedSsid = scanThread->getSsidFromNetworkName(wantedNetwork);
++ break;
++ }
++ }
++ }
++ NSDictionary *scanParameters = [NSDictionary dictionaryWithObjectsAndKeys:
++ [NSNumber numberWithBool:YES], kCWScanKeyMerge,
++ [NSNumber numberWithInt:kCWScanTypeFast], kCWScanKeyScanType,
++ [NSNumber numberWithInteger:100], kCWScanKeyRestTime,
++ (NSString *)QCFString::toCFStringRef(wantedSsid), kCWScanKeySSID,
++ nil];
++
++ NSArray *scanArray = [wifiInterface scanForNetworksWithParameters:scanParameters error:&err];
++
++ if(!err) {
++ for(uint row=0; row < [scanArray count]; row++ ) {
++ CWNetwork *apNetwork = [scanArray objectAtIndex:row];
++
++ if(wantedSsid == QCFString::toQString(CFStringRef([apNetwork ssid]))) {
++
++ if(!using8021X) {
++ SecKeychainAttribute attributes[3];
++
++ NSString *account = [apNetwork ssid];
++ NSString *keyKind = @"AirPort network password";
++ NSString *keyName = account;
++
++ attributes[0].tag = kSecAccountItemAttr;
++ attributes[0].data = (void *)[account UTF8String];
++ attributes[0].length = [account length];
++
++ attributes[1].tag = kSecDescriptionItemAttr;
++ attributes[1].data = (void *)[keyKind UTF8String];
++ attributes[1].length = [keyKind length];
++
++ attributes[2].tag = kSecLabelItemAttr;
++ attributes[2].data = (void *)[keyName UTF8String];
++ attributes[2].length = [keyName length];
++
++ SecKeychainAttributeList attributeList = {3,attributes};
++
++ SecKeychainSearchRef searchRef;
++ SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributeList, &searchRef);
++
++ NSString *password = @"";
++ SecKeychainItemRef searchItem;
++
++ if (SecKeychainSearchCopyNext(searchRef, &searchItem) == noErr) {
++ UInt32 realPasswordLength;
++ SecKeychainAttribute attributesW[8];
++ attributesW[0].tag = kSecAccountItemAttr;
++ SecKeychainAttributeList listW = {1,attributesW};
++ char *realPassword;
++ OSStatus status = SecKeychainItemCopyContent(searchItem, NULL, &listW, &realPasswordLength,(void **)&realPassword);
++
++ if (status == noErr) {
++ if (realPassword != NULL) {
++
++ QByteArray pBuf;
++ pBuf.resize(realPasswordLength);
++ pBuf.prepend(realPassword);
++ pBuf.insert(realPasswordLength,'\0');
++
++ password = [NSString stringWithUTF8String:pBuf];
++ }
++ SecKeychainItemFreeContent(&listW, realPassword);
++ }
++
++ CFRelease(searchItem);
++ } else {
++ qDebug() << "SecKeychainSearchCopyNext error";
++ }
++ [params setValue: password forKey: kCWAssocKeyPassphrase];
++ } // end using8021X
++
++
++ bool result = [wifiInterface associateToNetwork: apNetwork parameters:[NSDictionary dictionaryWithDictionary:params] error:&err];
++
++ if(!err) {
++ if(!result) {
++ emit connectionError(id, ConnectError);
++ } else {
++ return;
++ }
++ } else {
++ qDebug() <<"associate ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ]));
++ }
++ }
++ } //end scan network
++ } else {
++ qDebug() <<"scan ERROR"<< QCFString::toQString(CFStringRef([err localizedDescription ]));
++ }
++ emit connectionError(id, InterfaceLookupError);
++ }
++
++ locker.unlock();
++ emit connectionError(id, InterfaceLookupError);
++ [autoreleasepool release];
++}
++
++void QCoreWlanEngine::disconnectFromId(const QString &id)
++{
++ QMutexLocker locker(&mutex);
++
++ QString interfaceString = getInterfaceFromId(id);
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++
++ CWInterface *wifiInterface =
++ [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(interfaceString)];
++
++ [wifiInterface disassociate];
++ if ([[wifiInterface interfaceState]intValue] != kCWInterfaceStateInactive) {
++ locker.unlock();
++ emit connectionError(id, DisconnectionError);
++ locker.relock();
++ }
++ [autoreleasepool release];
++}
++
++void QCoreWlanEngine::requestUpdate()
++{
++ scanThread->getUserConfigurations();
++ doRequestUpdate();
++}
++
++void QCoreWlanEngine::doRequestUpdate()
++{
++ QMutexLocker locker(&mutex);
++
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++
++ NSArray *wifiInterfaces = [CWInterface supportedInterfaces];
++ for (uint row = 0; row < [wifiInterfaces count]; ++row) {
++ scanThread->interfaceName = QCFString::toQString(CFStringRef([wifiInterfaces objectAtIndex:row]));
++ scanThread->start();
++ }
++ locker.unlock();
++ [autoreleasepool release];
++}
++
++bool QCoreWlanEngine::isWifiReady(const QString &wifiDeviceName)
++{
++ QMutexLocker locker(&mutex);
++ bool haswifi = false;
++ if(hasWifi) {
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++ CWInterface *defaultInterface = [CWInterface interfaceWithName: (NSString *)QCFString::toCFStringRef(wifiDeviceName)];
++ if([defaultInterface power]) {
++ haswifi = true;
++ }
++ [autoreleasepool release];
++ }
++ return haswifi;
++}
++
++
++QNetworkSession::State QCoreWlanEngine::sessionStateForId(const QString &id)
++{
++ QMutexLocker locker(&mutex);
++ QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
++
++ if (!ptr)
++ return QNetworkSession::Invalid;
++
++ if (!ptr->isValid) {
++ return QNetworkSession::Invalid;
++ } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
++ return QNetworkSession::Connected;
++ } else if ((ptr->state & QNetworkConfiguration::Discovered) ==
++ QNetworkConfiguration::Discovered) {
++ return QNetworkSession::Disconnected;
++ } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) {
++ return QNetworkSession::NotAvailable;
++ } else if ((ptr->state & QNetworkConfiguration::Undefined) ==
++ QNetworkConfiguration::Undefined) {
++ return QNetworkSession::NotAvailable;
++ }
++
++ return QNetworkSession::Invalid;
++}
++
++QNetworkConfigurationManager::Capabilities QCoreWlanEngine::capabilities() const
++{
++ return QNetworkConfigurationManager::ForcedRoaming;
++}
++
++void QCoreWlanEngine::startNetworkChangeLoop()
++{
++
++ SCDynamicStoreContext dynStoreContext = { 0, this/*(void *)storeSession*/, NULL, NULL, NULL };
++ storeSession = SCDynamicStoreCreate(NULL,
++ CFSTR("networkChangeCallback"),
++ networkChangeCallback,
++ &dynStoreContext);
++ if (!storeSession ) {
++ qWarning() << "could not open dynamic store: error:" << SCErrorString(SCError());
++ return;
++ }
++
++ CFMutableArrayRef notificationKeys;
++ notificationKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
++ CFMutableArrayRef patternsArray;
++ patternsArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
++
++ CFStringRef storeKey;
++ storeKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
++ kSCDynamicStoreDomainState,
++ kSCEntNetIPv4);
++ CFArrayAppendValue(notificationKeys, storeKey);
++ CFRelease(storeKey);
++
++ storeKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
++ kSCDynamicStoreDomainState,
++ kSCCompAnyRegex,
++ kSCEntNetIPv4);
++ CFArrayAppendValue(patternsArray, storeKey);
++ CFRelease(storeKey);
++
++ if (!SCDynamicStoreSetNotificationKeys(storeSession , notificationKeys, patternsArray)) {
++ qWarning() << "register notification error:"<< SCErrorString(SCError());
++ CFRelease(storeSession );
++ CFRelease(notificationKeys);
++ CFRelease(patternsArray);
++ return;
++ }
++ CFRelease(notificationKeys);
++ CFRelease(patternsArray);
++
++ runloopSource = SCDynamicStoreCreateRunLoopSource(NULL, storeSession , 0);
++ if (!runloopSource) {
++ qWarning() << "runloop source error:"<< SCErrorString(SCError());
++ CFRelease(storeSession );
++ return;
++ }
++
++ CFRunLoopAddSource(CFRunLoopGetCurrent(), runloopSource, kCFRunLoopDefaultMode);
++ return;
++}
++
++QNetworkSessionPrivate *QCoreWlanEngine::createSessionBackend()
++{
++ return new QNetworkSessionPrivateImpl;
++}
++
++QNetworkConfigurationPrivatePointer QCoreWlanEngine::defaultConfiguration()
++{
++ return QNetworkConfigurationPrivatePointer();
++}
++
++bool QCoreWlanEngine::requiresPolling() const
++{
++ return true;
++}
++
++void QCoreWlanEngine::networksChanged()
++{
++ QMutexLocker locker(&mutex);
++
++ QStringList previous = accessPointConfigurations.keys();
++
++ QList foundConfigurations = scanThread->getConfigurations();
++ while (!foundConfigurations.isEmpty()) {
++ QNetworkConfigurationPrivate *cpPriv = foundConfigurations.takeFirst();
++
++ previous.removeAll(cpPriv->id);
++
++ if (accessPointConfigurations.contains(cpPriv->id)) {
++ QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(cpPriv->id);
++
++ bool changed = false;
++
++ ptr->mutex.lock();
++
++ if (ptr->isValid != cpPriv->isValid) {
++ ptr->isValid = cpPriv->isValid;
++ changed = true;
++ }
++
++ if (ptr->name != cpPriv->name) {
++ ptr->name = cpPriv->name;
++ changed = true;
++ }
++
++ if (ptr->bearerType != cpPriv->bearerType) {
++ ptr->bearerType = cpPriv->bearerType;
++ changed = true;
++ }
++
++ if (ptr->state != cpPriv->state) {
++ ptr->state = cpPriv->state;
++ changed = true;
++ }
++
++ ptr->mutex.unlock();
++
++ if (changed) {
++ locker.unlock();
++ emit configurationChanged(ptr);
++ locker.relock();
++ }
++
++ delete cpPriv;
++ } else {
++ QNetworkConfigurationPrivatePointer ptr(cpPriv);
++
++ accessPointConfigurations.insert(ptr->id, ptr);
++
++ locker.unlock();
++ emit configurationAdded(ptr);
++ locker.relock();
++ }
++ }
++
++ while (!previous.isEmpty()) {
++ QNetworkConfigurationPrivatePointer ptr =
++ accessPointConfigurations.take(previous.takeFirst());
++
++ locker.unlock();
++ emit configurationRemoved(ptr);
++ locker.relock();
++ }
++
++ locker.unlock();
++ emit updateCompleted();
++
++}
++
++quint64 QCoreWlanEngine::bytesWritten(const QString &id)
++{
++ QMutexLocker locker(&mutex);
++ const QString interfaceStr = getInterfaceFromId(id);
++ return getBytes(interfaceStr,false);
++}
++
++quint64 QCoreWlanEngine::bytesReceived(const QString &id)
++{
++ QMutexLocker locker(&mutex);
++ const QString interfaceStr = getInterfaceFromId(id);
++ return getBytes(interfaceStr,true);
++}
++
++quint64 QCoreWlanEngine::startTime(const QString &identifier)
++{
++ QMutexLocker locker(&mutex);
++ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
++ quint64 timestamp = 0;
++
++ NSString *filePath = @"/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist";
++ NSDictionary* plistDict = [[[NSDictionary alloc] initWithContentsOfFile:filePath] autorelease];
++ if(plistDict == nil)
++ return timestamp;
++ NSString *input = @"KnownNetworks";
++ NSString *timeStampStr = @"_timeStamp";
++
++ NSString *ssidStr = @"SSID_STR";
++
++ for (id key in plistDict) {
++ if ([input isEqualToString:key]) {
++
++ NSDictionary *knownNetworksDict = [plistDict objectForKey:key];
++ if(knownNetworksDict == nil)
++ return timestamp;
++ for (id networkKey in knownNetworksDict) {
++ bool isFound = false;
++ NSDictionary *itemDict = [knownNetworksDict objectForKey:networkKey];
++ if(itemDict == nil)
++ return timestamp;
++ NSInteger dictSize = [itemDict count];
++ id objects[dictSize];
++ id keys[dictSize];
++
++ [itemDict getObjects:objects andKeys:keys];
++ bool ok = false;
++ for(int i = 0; i < dictSize; i++) {
++ if([ssidStr isEqualToString:keys[i]]) {
++ const QString ident = QString::number(qHash(QLatin1String("corewlan:") + QCFString::toQString(CFStringRef(objects[i]))));
++ if(ident == identifier) {
++ ok = true;
++ }
++ }
++ if(ok && [timeStampStr isEqualToString:keys[i]]) {
++ timestamp = (quint64)[objects[i] timeIntervalSince1970];
++ isFound = true;
++ break;
++ }
++ }
++ if(isFound)
++ break;
++ }
++ }
++ }
++ [autoreleasepool release];
++ return timestamp;
++}
++
++quint64 QCoreWlanEngine::getBytes(const QString &interfaceName, bool b)
++{
++ struct ifaddrs *ifAddressList, *ifAddress;
++ struct if_data *if_data;
++
++ quint64 bytes = 0;
++ ifAddressList = nil;
++ if(getifaddrs(&ifAddressList) == 0) {
++ for(ifAddress = ifAddressList; ifAddress; ifAddress = ifAddress->ifa_next) {
++ if(interfaceName == ifAddress->ifa_name) {
++ if_data = (struct if_data*)ifAddress->ifa_data;
++ if(b) {
++ bytes = if_data->ifi_ibytes;
++ break;
++ } else {
++ bytes = if_data->ifi_obytes;
++ break;
++ }
++ }
++ }
++ freeifaddrs(ifAddressList);
++ }
++ return bytes;
++}
++
++QT_END_NAMESPACE
From 8c060691d732c95ca5868a26334a1c0381e0f586 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Thu, 5 Dec 2013 12:21:10 -0500
Subject: [PATCH 23/66] Update Mac deployment target to make it build on 10.9,
apply Qt4 patches
---
scripts/macosx-build-dependencies.sh | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index 19c97097..d4ca1f7c 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -24,7 +24,7 @@ BASEDIR=$PWD/../libraries
OPENSCADDIR=$PWD
SRCDIR=$BASEDIR/src
DEPLOYDIR=$BASEDIR/install
-MAC_OSX_VERSION_MIN=10.6
+MAC_OSX_VERSION_MIN=10.7
OPTION_32BIT=false
OPTION_LLVM=false
OPTION_CLANG=false
@@ -54,6 +54,9 @@ build_qt()
fi
tar xzf qt-everywhere-opensource-src-$version.tar.gz
cd qt-everywhere-opensource-src-$version
+ patch -p0 < $OPENSCADDIR/patches/qt4/patch-src_corelib_global_qglobal.h.diff
+ patch -p0 < $OPENSCADDIR/patches/qt4/patch-libtiff.diff
+ patch -p0 < $OPENSCADDIR/patches/qt4/patch-src_plugins_bearer_corewlan_qcorewlanengine.mm.diff
if $USING_CLANG; then
# FIX for clang
sed -i "" -e "s/::TabletProximityRec/TabletProximityRec/g" src/gui/kernel/qt_cocoa_helpers_mac_p.h
@@ -220,7 +223,7 @@ build_boost()
BOOST_TOOLSET="toolset=clang"
echo "using clang ;" >> tools/build/v2/user-config.jam
fi
- ./b2 -d+2 $BOOST_TOOLSET cflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS" linkflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS -headerpad_max_install_names" install
+ ./b2 -j6 -d+2 $BOOST_TOOLSET cflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS" linkflags="-mmacosx-version-min=$MAC_OSX_VERSION_MIN -arch x86_64 $BOOST_EXTRA_FLAGS -headerpad_max_install_names" install
install_name_tool -id $DEPLOYDIR/lib/libboost_thread.dylib $DEPLOYDIR/lib/libboost_thread.dylib
install_name_tool -change libboost_system.dylib $DEPLOYDIR/lib/libboost_system.dylib $DEPLOYDIR/lib/libboost_thread.dylib
install_name_tool -change libboost_chrono.dylib $DEPLOYDIR/lib/libboost_chrono.dylib $DEPLOYDIR/lib/libboost_thread.dylib
From ac7b37a3d67662b99adb2648945f7c972624344c Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Fri, 6 Dec 2013 09:56:22 +1100
Subject: [PATCH 24/66] Update comments/messages for CGAL source of bad boost
libraries
Add comments and change to a status instead of a warning (as we recover
nicely and it is a known issue in CGAL @
https://bugs.launchpad.net/ubuntu/+source/cgal/+bug/1242111)
---
tests/CMakeLists.txt | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index fd5097a8..65eefcda 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -376,23 +376,24 @@ if("${CGAL_MAJOR_VERSION}.${CGAL_MINOR_VERSION}" VERSION_LESS 3.6)
message(FATAL_ERROR "CGAL >= 3.6 required")
endif()
inclusion(CGAL_DIR CGAL_INCLUDE_DIRS)
-
-#Get rid of bad libraries suggested for BOOST dependencies (they don't exist on some machines and cause build failures).
-#/usr/lib/libboost_thread.so;/usr/lib/libboost_system.so;
+#Remove bad BOOST libraries from CGAL 3rd party dependencies when they don't exist (such as on 64-bit Ubuntu 13.10).
+#Libs of concern are /usr/lib/libboost_thread.so;/usr/lib/libboost_system.so;
+#Confirmed bug in CGAL @ https://bugs.launchpad.net/ubuntu/+source/cgal/+bug/1242111
string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_system.so" FIND_POSITION )
if(NOT "-1" STREQUAL ${FIND_POSITION} )
-if(NOT EXISTS "/usr/lib/libboost_system.so")
- MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_system.so -- stripping" )
- string(REPLACE "/usr/lib/libboost_system.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
-endif()
+ if(NOT EXISTS "/usr/lib/libboost_system.so")
+ MESSAGE( STATUS "CGAL_3RD_PARTY_LIBRARIES:Removing non-existent /usr/lib/libboost_system.so" )
+ string(REPLACE "/usr/lib/libboost_system.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+ endif()
endif()
string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_thread.so" FIND_POSITION )
if(NOT "-1" STREQUAL ${FIND_POSITION} )
-if(NOT EXISTS "/usr/lib/libboost_thread.so")
- MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_thread.so -- stripping" )
- string(REPLACE "/usr/lib/libboost_thread.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
-endif()
+ if(NOT EXISTS "/usr/lib/libboost_thread.so")
+ MESSAGE( STATUS "CGAL_3RD_PARTY_LIBRARIES:Removing non-existent /usr/lib/libboost_thread.so" )
+ string(REPLACE "/usr/lib/libboost_thread.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+ endif()
endif()
+
if(${CMAKE_CXX_COMPILER} MATCHES ".*clang.*" AND NOT ${CGAL_CXX_FLAGS_INIT} STREQUAL "" )
string(REPLACE "-frounding-math" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
string(REPLACE "--param=ssp-buffer-size=4" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
From 38a342215970396a5590281cd66d0ca9c9ab7e98 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Fri, 6 Dec 2013 00:39:21 -0500
Subject: [PATCH 25/66] #559 fixes for 10.9
---
tests/CMakeLists.txt | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0477a45d..b84775b8 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,6 +1,6 @@
# instructions - see ../doc/testing.txt
-# set(DEBUG_OSCD 1) # print debug info during cmake
+#set(DEBUG_OSCD 1) # print debug info during cmake
cmake_minimum_required(VERSION 2.8)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
@@ -14,9 +14,16 @@ include(CMakeParseArguments.cmake)
# Detect Lion and force gcc
IF (APPLE)
+ # Somehow, since we build dependencies for 10.7, we need to also build executables
+ # for 10.7. This used to not be necessary, but since 10.9 it apparently is..
+ SET(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
EXECUTE_PROCESS(COMMAND sw_vers -productVersion OUTPUT_VARIABLE MACOSX_VERSION)
- IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0")
- message("Detected Mountain Lion (10.8) or later")
+ IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.9.0")
+ message("Detected Maverick (10.9) or later")
+ set(CMAKE_C_COMPILER "clang")
+ set(CMAKE_CXX_COMPILER "clang++")
+ ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0")
+ message("Detected Mountain Lion (10.8)")
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.7.0")
From e3317ecc64659ae8be2a7b02d4df17dad6d875db Mon Sep 17 00:00:00 2001
From: "David Eccles (gringer)"
Date: Fri, 6 Dec 2013 13:14:09 +1300
Subject: [PATCH 26/66] Fail if any polygon points for rotate_extrude are less
than 0
---
src/PolySetCGALEvaluator.cc | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index bc9206f3..e5eba2b9 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -457,7 +457,13 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD
{
double max_x = 0;
for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) {
- max_x = fmax(max_x, dxf.points[dxf.paths[i].indices[j]][0]);
+ double point_x = dxf.points[dxf.paths[i].indices[j]][0];
+ if(point_x < 0){
+ PRINT("ERROR: all points for rotate_extrude() must have non-negative X coordinates");
+ PRINT((boost::format("[Point %d on path %d has X coordinate %f]") % j % i % point_x).str());
+ return NULL;
+ }
+ max_x = fmax(max_x, point_x);
}
int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa);
From ede5c4b6882692dc28fc7017c4aeb87ec9222f8e Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Fri, 6 Dec 2013 00:52:51 -0500
Subject: [PATCH 27/66] delete ps when short-circuiting return, no need for
explicit boost::format
---
src/PolySetCGALEvaluator.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index e5eba2b9..599fd7f5 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -458,9 +458,10 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD
double max_x = 0;
for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) {
double point_x = dxf.points[dxf.paths[i].indices[j]][0];
- if(point_x < 0){
+ if (point_x < 0) {
PRINT("ERROR: all points for rotate_extrude() must have non-negative X coordinates");
- PRINT((boost::format("[Point %d on path %d has X coordinate %f]") % j % i % point_x).str());
+ PRINTB("[Point %d on path %d has X coordinate %f]", j % i % point_x);
+ delete ps;
return NULL;
}
max_x = fmax(max_x, point_x);
From a0d8cbe692a5a474a7bc284cded4a01894b25c2a Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Fri, 6 Dec 2013 17:46:52 +1100
Subject: [PATCH 28/66] Add in missed glib build dependencies for OS X and unix
---
scripts/macosx-build-dependencies.sh | 25 +++++++++++++++++++++++++
scripts/uni-build-dependencies.sh | 25 +++++++++++++++++++++++++
2 files changed, 50 insertions(+)
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index 19c97097..81985bc7 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -282,6 +282,30 @@ build_glew()
make GLEW_DEST=$DEPLOYDIR CC=$CC CFLAGS.EXTRA="-no-cpp-precomp -dynamic -fno-common -mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" LDFLAGS.EXTRA="-mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" STRIP= install
}
+build_glib2()
+{
+ version="$1"
+ maj_min_version="${version%.*}" #Drop micro
+
+ if [ -e $DEPLOYDIR/lib/glib-2.0 ]; then
+ echo "glib2 already installed. not building"
+ return
+ fi
+
+ echo "Building glib2 $version..."
+ cd "$BASEDIR"/src
+ rm -rf "glib-$version"
+ if [ ! -f "glib-$version.tar.xz" ]; then
+ curl --insecure -LO "http://ftp.gnome.org/pub/gnome/sources/glib/$maj_min_version/glib-$version.tar.xz"
+ fi
+ tar xJf "glib-$version.tar.xz"
+ cd "glib-$version"
+
+ ./configure --prefix="$DEPLOYDIR"
+ make -j$NUMCPU
+ make install
+}
+
build_opencsg()
{
version=$1
@@ -446,6 +470,7 @@ build_boost 1.54.0
# NB! For CGAL, also update the actual download URL in the function
build_cgal 4.3
build_glew 1.10.0
+build_glib2 2.38.1
build_opencsg 1.3.2
if $OPTION_DEPLOY; then
# build_sparkle andymatuschak 0ed83cf9f2eeb425d4fdd141c01a29d843970c20
diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh
index 8d912c35..ba328b7e 100755
--- a/scripts/uni-build-dependencies.sh
+++ b/scripts/uni-build-dependencies.sh
@@ -409,6 +409,31 @@ build_glew()
GLEW_DEST=$DEPLOYDIR $MAKER install
}
+build_glib2()
+{
+ version="$1"
+ maj_min_version="${version%.*}" #Drop micro
+
+ if [ -e $DEPLOYDIR/lib/glib-2.0 ]; then
+echo "glib2 already installed. not building"
+ return
+fi
+
+echo "Building glib2 $version..."
+ cd "$BASEDIR"/src
+ rm -rf "glib-$version"
+ if [ ! -f "glib-$version.tar.xz" ]; then
+curl --insecure -LO "http://ftp.gnome.org/pub/gnome/sources/glib/$maj_min_version/glib-$version.tar.xz"
+ fi
+tar xJf "glib-$version.tar.xz"
+ cd "glib-$version"
+
+ ./configure --prefix="$DEPLOYDIR"
+ make -j$NUMCPU
+ make install
+
+}
+
build_opencsg()
{
if [ -e $DEPLOYDIR/lib/libopencsg.so ]; then
From d7d5bea7363703c76b9787598304bfc838e893ee Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Fri, 6 Dec 2013 18:33:42 +1100
Subject: [PATCH 29/66] Add specific tests for unicode len()
---
testdata/scad/misc/string-unicode.scad | 10 +++++++++-
tests/regression/echotest/string-unicode-expected.echo | 3 +++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/testdata/scad/misc/string-unicode.scad b/testdata/scad/misc/string-unicode.scad
index d8e3e5c9..1386d63d 100644
--- a/testdata/scad/misc/string-unicode.scad
+++ b/testdata/scad/misc/string-unicode.scad
@@ -1,3 +1,12 @@
+//Test length reporting
+text_1bytes_len = "1234";
+text_2bytes_len = "ЛЛЛЛ";
+text_4bytes_len = "🂡🂱🃁🃑";
+
+echo( "text_1bytes_len = ", text_1bytes_len, " len = ", len(text_1bytes_len) );
+echo( "text_2bytes_len = ", text_2bytes_len, " len = ", len(text_2bytes_len) );
+echo( "text_4bytes_len = ", text_4bytes_len, " len = ", len(text_4bytes_len) );
+
//Test how well arrays of unicode string are accessed.
texts_array = [
@@ -33,4 +42,3 @@ echo( "Past end of unicode only 4-byte ", text_4bytes[len(text_4bytes)] );
echo( "Past end of both 2-byte ", text_2bytes[ len(text_2bytes) * 2 ] );
echo( "Past end of both 4-byte ", text_4bytes[ len(text_4bytes) * 4 ] );
-
diff --git a/tests/regression/echotest/string-unicode-expected.echo b/tests/regression/echotest/string-unicode-expected.echo
index b4b848fd..a1cd3bec 100644
--- a/tests/regression/echotest/string-unicode-expected.echo
+++ b/tests/regression/echotest/string-unicode-expected.echo
@@ -1,3 +1,6 @@
+ECHO: "text_1bytes_len = ", "1234", " len = ", 4
+ECHO: "text_2bytes_len = ", "ЛЛЛЛ", " len = ", 4
+ECHO: "text_4bytes_len = ", "🂡🂱🃁🃑", " len = ", 4
ECHO: "[", 0, "] = ", "DEADBEEF", " of len=", 8, ":"
ECHO: " [", 0, "]=", "D"
ECHO: " [", 1, "]=", "E"
From 40ae8c7b343fc5dafc8c9bdc7d8a63f6e8032fca Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sat, 7 Dec 2013 14:38:22 -0800
Subject: [PATCH 30/66] Add html report upload to test_pretty_print.py, removed
wiki support
---
tests/test_pretty_print.py | 878 ++++++++++++++++---------------------
1 file changed, 381 insertions(+), 497 deletions(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index a31b1a86..c26b919d 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -25,544 +25,428 @@
# todo
#
-# 1. Note: Wiki code is deprecated. All future development should move to
-# create html output (or even xml output). Wiki design was based on the
-# wrong assumption of easily accessible public wiki servers with
-# auto-upload scripts available. wiki code should be removed and code
-# simplified.
-#
-# to still use wiki, use args '--wiki' and/or '--wiki-upload'
-#
-# 2. why is hash differing
+# 1. why is hash differing
-import string,sys,re,os,hashlib,subprocess,textwrap,time,platform
+import string
+import sys
+import re
+import os
+import hashlib
+import subprocess
+import time
+import platform
def tryread(filename):
- data = None
- try:
- f = open(filename,'rb')
- data = f.read()
- f.close()
- except Exception, e:
- print 'couldn\'t open ',filename
- print type(e), e
- return data
+ data = None
+ try:
+ f = open(filename,'rb')
+ data = f.read()
+ f.close()
+ except Exception as e:
+ print 'couldn\'t open ',filename
+ print type(e), e
+ return data
-def trysave(filename,data):
- dir = os.path.dirname(filename)
- try:
- if not os.path.isdir(dir):
- if not dir == '':
- debug( 'creating' + dir)
- os.mkdir(dir)
- f=open(filename,'wb')
- f.write(data)
- f.close()
- except Exception, e:
- print 'problem writing to',filename
- print type(e), e
- return None
- return True
+def trysave(filename, data):
+ dir = os.path.dirname(filename)
+ try:
+ if not os.path.isdir(dir):
+ if not dir == '':
+ debug( 'creating' + dir)
+ os.mkdir(dir)
+ f=open(filename,'wb')
+ f.write(data)
+ f.close()
+ except Exception as e:
+ print 'problem writing to',filename
+ print type(e), e
+ return None
+ return True
-def ezsearch(pattern,str):
- x = re.search(pattern,str,re.DOTALL|re.MULTILINE)
- if x and len(x.groups())>0: return x.group(1).strip()
- return ''
-
+def ezsearch(pattern, str):
+ x = re.search(pattern,str,re.DOTALL|re.MULTILINE)
+ if x and len(x.groups())>0: return x.group(1).strip()
+ return ''
+
def read_gitinfo():
- # won't work if run from outside of branch.
- try:
- data = subprocess.Popen(['git','remote','-v'],stdout=subprocess.PIPE).stdout.read()
- origin = ezsearch('^origin *?(.*?)\(fetch.*?$',data)
- upstream = ezsearch('^upstream *?(.*?)\(fetch.*?$',data)
- data = subprocess.Popen(['git','branch'],stdout=subprocess.PIPE).stdout.read()
- branch = ezsearch('^\*(.*?)$',data)
- out = 'Git branch: ' + branch + ' from origin ' + origin + '\n'
- out += 'Git upstream: ' + upstream + '\n'
- except:
- out = 'Problem running git'
- return out
+ # won't work if run from outside of branch.
+ try:
+ data = subprocess.Popen(['git', 'remote', '-v'], stdout=subprocess.PIPE).stdout.read()
+ origin = ezsearch('^origin *?(.*?)\(fetch.*?$', data)
+ upstream = ezsearch('^upstream *?(.*?)\(fetch.*?$', data)
+ data = subprocess.Popen(['git', 'branch'], stdout=subprocess.PIPE).stdout.read()
+ branch = ezsearch('^\*(.*?)$', data)
+ out = 'Git branch: ' + branch + ' from origin ' + origin + '\n'
+ out += 'Git upstream: ' + upstream + '\n'
+ except:
+ out = 'Problem running git'
+ return out
def read_sysinfo(filename):
- data = tryread(filename)
- if not data:
- sinfo = platform.sys.platform
- sinfo += '\nsystem cannot create offscreen GL framebuffer object'
- sinfo += '\nsystem cannot create GL based images'
- sysid = platform.sys.platform+'_no_GL_renderer'
- return sinfo, sysid
+ data = tryread(filename)
+ if not data:
+ sinfo = platform.sys.platform
+ sinfo += '\nsystem cannot create offscreen GL framebuffer object'
+ sinfo += '\nsystem cannot create GL based images'
+ sysid = platform.sys.platform+'_no_GL_renderer'
+ return sinfo, sysid
- machine = ezsearch('Machine:(.*?)\n',data)
- machine = machine.replace(' ','-').replace('/','-')
+ machine = ezsearch('Machine:(.*?)\n',data)
+ machine = machine.replace(' ','-').replace('/','-')
- osinfo = ezsearch('OS info:(.*?)\n',data)
- osplain = osinfo.split(' ')[0].strip().replace('/','-')
- if 'windows' in osinfo.lower(): osplain = 'win'
+ osinfo = ezsearch('OS info:(.*?)\n',data)
+ osplain = osinfo.split(' ')[0].strip().replace('/','-')
+ if 'windows' in osinfo.lower():
+ osplain = 'win'
- renderer = ezsearch('GL Renderer:(.*?)\n',data)
- tmp = renderer.split(' ')
- tmp = string.join(tmp[0:3],'-')
- tmp = tmp.split('/')[0]
- renderer = tmp
+ renderer = ezsearch('GL Renderer:(.*?)\n',data)
+ tmp = renderer.split(' ')
+ tmp = string.join(tmp[0:3],'-')
+ tmp = tmp.split('/')[0]
+ renderer = tmp
- data += read_gitinfo()
+ data += read_gitinfo()
- data += 'Image comparison: ImageMagick'
+ data += 'Image comparison: ImageMagick'
- data = data.strip()
+ data = data.strip()
- # create 4 letter hash and stick on end of sysid
- nondate_data = re.sub("\n.*?ompile date.*?\n","\n",data).strip()
- hexhash = hashlib.md5()
- hexhash.update(nondate_data)
- hexhash = hexhash.hexdigest()[-4:].upper()
- hash = ''
- for c in hexhash: hash += chr(ord(c)+97-48)
+ # create 4 letter hash and stick on end of sysid
+ nondate_data = re.sub("\n.*?ompile date.*?\n", "\n", data).strip()
+ hexhash = hashlib.md5(nondate_data).hexdigest()[-4:].upper()
+ hash_ = ''.join(chr(ord(c) + 97 - 48) for c in hexhash)
- sysid = osplain + '_' + machine + '_' + renderer + '_' + hash
- sysid = sysid.replace('(','_').replace(')','_')
- sysid = sysid.lower()
+ sysid = '_'.join([osplain, machine, renderer, hash_])
+ sysid = sysid.replace('(', '_').replace(')', '_')
+ sysid = sysid.lower()
- return data, sysid
+ return data, sysid
class Test:
- def __init__(self,fullname,time,passed,output,type,actualfile,expectedfile,scadfile,log):
- self.fullname,self.time,self.passed,self.output = \
- fullname, time, passed, output
- self.type, self.actualfile, self.expectedfile, self.scadfile = \
- type, actualfile, expectedfile, scadfile
- self.fulltestlog = log
-
- def __str__(self):
- x = 'fullname: ' + self.fullname
- x+= '\nactualfile: ' + self.actualfile
- x+= '\nexpectedfile: ' + self.expectedfile
- x+= '\ntesttime: ' + self.time
- x+= '\ntesttype: ' + self.type
- x+= '\npassed: ' + str(self.passed)
- x+= '\nscadfile: ' + self.scadfile
- x+= '\noutput bytes: ' + str(len(self.output))
- x+= '\ntestlog bytes: ' + str(len(self.fulltestlog))
- x+= '\n'
- return x
+ def __init__(self, fullname, subpr, passed, output, type, actualfile,
+ expectedfile, scadfile, log):
+ self.fullname, self.time = fullname, time
+ self.passed, self.output = passed, output
+ self.type, self.actualfile = type, actualfile
+ self.expectedfile, self.scadfile = expectedfile, scadfile
+ self.fulltestlog = log
+
+ def __str__(self):
+ x = 'fullname: ' + self.fullname
+ x+= '\nactualfile: ' + self.actualfile
+ x+= '\nexpectedfile: ' + self.expectedfile
+ x+= '\ntesttime: ' + self.time
+ x+= '\ntesttype: ' + self.type
+ x+= '\npassed: ' + str(self.passed)
+ x+= '\nscadfile: ' + self.scadfile
+ x+= '\noutput bytes: ' + str(len(self.output))
+ x+= '\ntestlog bytes: ' + str(len(self.fulltestlog))
+ x+= '\n'
+ return x
def parsetest(teststring):
- patterns = ["Test:(.*?)\n", # fullname
- "Test time =(.*?) sec\n",
- "Test time.*?Test (Passed)", # pass/fail
- "Output:(.*?)",
- 'Command:.*?-s" "(.*?)"', # type
- "^ actual .*?:(.*?)\n",
- "^ expected .*?:(.*?)\n",
- 'Command:.*?(testdata.*?)"' # scadfile
- ]
- hits = map( lambda pattern: ezsearch(pattern,teststring), patterns )
- test = Test(hits[0],hits[1],hits[2]=='Passed',hits[3],hits[4],hits[5],hits[6],hits[7],teststring)
- if len(test.actualfile) > 0: test.actualfile_data = tryread(test.actualfile)
- if len(test.expectedfile) > 0: test.expectedfile_data = tryread(test.expectedfile)
- return test
+ patterns = ["Test:(.*?)\n", # fullname
+ "Test time =(.*?) sec\n",
+ "Test time.*?Test (Passed)", # pass/fail
+ "Output:(.*?)",
+ 'Command:.*?-s" "(.*?)"', # type
+ "^ actual .*?:(.*?)\n",
+ "^ expected .*?:(.*?)\n",
+ 'Command:.*?(testdata.*?)"' # scadfile
+ ]
+ hits = map( lambda pattern: ezsearch(pattern, teststring), patterns)
+ test = Test(hits[0], hits[1], hits[2]=='Passed', hits[3], hits[4], hits[5],
+ hits[6], hits[7], teststring)
+ if len(test.actualfile) > 0:
+ test.actualfile_data = tryread(test.actualfile)
+ if len(test.expectedfile) > 0:
+ test.expectedfile_data = tryread(test.expectedfile)
+ return test
def parselog(data):
- startdate = ezsearch('Start testing: (.*?)\n',data)
- enddate = ezsearch('End testing: (.*?)\n',data)
- pattern = '([0-9]*/[0-9]* Testing:.*?time elapsed.*?\n)'
- test_chunks = re.findall(pattern,data,re.S)
- tests = map( parsetest, test_chunks )
- tests = sorted(tests, key = lambda t:t.passed)
- return startdate, tests, enddate
+ startdate = ezsearch('Start testing: (.*?)\n', data)
+ enddate = ezsearch('End testing: (.*?)\n', data)
+ pattern = '([0-9]*/[0-9]* Testing:.*?time elapsed.*?\n)'
+ test_chunks = re.findall(pattern,data, re.S)
+ tests = map( parsetest, test_chunks )
+ tests = sorted(tests, key = lambda t: t.passed)
+ return startdate, tests, enddate
def load_makefiles(builddir):
- filelist = []
- for root, dirs, files in os.walk(builddir):
- for fname in files: filelist += [ os.path.join(root, fname) ]
- files = filter(lambda x: 'build.make' in os.path.basename(x), filelist)
- files += filter(lambda x: 'flags.make' in os.path.basename(x), filelist)
- files = filter(lambda x: 'esting' not in x and 'emporary' not in x, files)
- result = {}
- for fname in files:
- result[fname.replace(builddir,'')] = open(fname,'rb').read()
- return result
-
-def wikify_filename(fname, wiki_rootpath, sysid):
- wikifname = fname.replace('/','_').replace('\\','_').strip('.')
- return wiki_rootpath + '_' + sysid + '_' + wikifname
-
-def towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
-
- wiki_template = """
-[[WIKI_ROOTPATH]] test run report
-
-'''Sysid''': SYSID
-
-'''Result summary''': NUMPASSED / NUMTESTS tests passed ( PERCENTPASSED % )
-
-'''System info''':
-
-SYSINFO
-
-
-start time: STARTDATE
-end time : ENDDATE
-
-'''Image tests'''
-
-
-{| border=1 cellspacing=0 cellpadding=1
-|-
-| colspan=2 | FTESTNAME
-|-
-| Expected image || Actual image
-|-
-| [[File:EXPECTEDFILE|250px]] || ACTUALFILE_WIKI
-|}
-
-
-TESTLOG
-
+ filelist = []
+ for root, dirs, files in os.walk(builddir):
+ for fname in files: filelist += [ os.path.join(root, fname) ]
+ files = [file for file in filelist if 'build.make' in os.path.basename(file)
+ or 'flags.make' in os.path.basename(file)]
+ files = [file for file in files if 'esting' not in file and 'emporary' not in file]
+ result = {}
+ for fname in files:
+ result[fname.replace(builddir, '')] = tryread(fname)
+ return result
+def png_encode64(fname, width=250, data=None):
+ # en.wikipedia.org/wiki/Data_URI_scheme
+ data = data or tryread(fname) or ''
+ data_uri = data.encode('base64').replace('\n', '')
+ tag = ''''''
+ if data == '':
+ alt = 'alt="error: no image generated" '
+ else:
+ alt = 'alt="openscad_test_image" '
+ tag %= (data_uri, width, alt)
+ return tag
-
-
-
-'''Text tests'''
-
-
-{|border=1 cellspacing=0 cellpadding=1
-|-
-| FTESTNAME
-|}
-
-
-TESTLOG
-
-
-
-
-
-'''build.make and flags.make'''
-
-*[[MAKEFILE_NAME]]
-
-"""
- txtpages = {}
- imgs = {}
- passed_tests = filter(lambda x: x.passed, tests)
- failed_tests = filter(lambda x: not x.passed, tests)
-
- tests_to_report = failed_tests
- if include_passed: tests_to_report = tests
-
- try: percent = str(int(100.0*len(passed_tests) / len(tests)))
- except ZeroDivisionError: percent = 'n/a'
- s = wiki_template
- repeat1 = ezsearch('(.*?)',s)
- repeat2 = ezsearch('(.*?)',s)
- repeat3 = ezsearch('(.*?)',s)
- dic = { 'STARTDATE': startdate, 'ENDDATE': enddate, 'WIKI_ROOTPATH': wiki_rootpath,
- 'SYSINFO': sysinfo, 'SYSID':sysid,
- 'NUMTESTS':len(tests), 'NUMPASSED':len(passed_tests), 'PERCENTPASSED':percent }
- for key in dic.keys():
- s = s.replace(key,str(dic[key]))
-
- for t in tests_to_report:
- if t.type in ('txt', 'ast', 'csg', 'term', 'echo'):
- newchunk = re.sub('FTESTNAME',t.fullname,repeat2)
- newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
- s = s.replace(repeat2, newchunk+repeat2)
- elif t.type=='png':
- tmp = t.actualfile.replace(builddir,'')
- wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
- tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
- wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
- if hasattr(t, 'expectedfile_data'):
- imgs[wikiname_e] = t.expectedfile_data
- if t.actualfile:
- actualfile_wiki = '[[File:'+wikiname_a+'|250px]]'
- if hasattr(t, 'actualfile_data'):
- imgs[wikiname_a] = t.actualfile_data
- else:
- actualfile_wiki = 'No image generated.'
- newchunk = re.sub('FTESTNAME',t.fullname,repeat1)
- newchunk = newchunk.replace('ACTUALFILE_WIKI',actualfile_wiki)
- newchunk = newchunk.replace('EXPECTEDFILE',wikiname_e)
- newchunk = newchunk.replace('TESTLOG',t.fulltestlog)
- s = s.replace(repeat1, newchunk+repeat1)
- else:
- raise Exception("Unknown test type %r"%t.type)
-
- makefiles_wikinames = {}
- for mf in sorted(makefiles.keys()):
- tmp = mf.replace('CMakeFiles','').replace('.dir','')
- wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
- newchunk = re.sub('MAKEFILE_NAME',wikiname,repeat3)
- s = s.replace(repeat3, newchunk+repeat3)
- makefiles_wikinames[mf] = wikiname
-
- s = s.replace(repeat1,'')
- s = s.replace(repeat2,'')
- s = s.replace(repeat3,'')
- s = re.sub('\n','',s)
- s = re.sub('','',s)
-
- mainpage_wikiname = wiki_rootpath + '_' + sysid + '_test_report'
- txtpages[ mainpage_wikiname ] = s
- for mf in sorted(makefiles.keys()):
- txtpages[ makefiles_wikinames[ mf ] ] = '\n*Subreport from [['+mainpage_wikiname+']]\n\n\n\n'+makefiles[mf]+'\n
'
-
- return imgs, txtpages
-
-def png_encode64( fname, width=250 ):
- # en.wikipedia.org/wiki/Data_URI_scheme
- try:
- f = open( fname, "rb" )
- data = f.read()
- except:
- data = ''
- data_uri = data.encode("base64").replace("\n","")
- tag = '\n'
- return tag
-
-def tohtml(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles):
- # kludge. assume wiki stuff has alreayd run and dumped files properly
- head = ''+wiki_rootpath+' test run for '+sysid +''
- tail = ''
-
- passed_tests = filter(lambda x: x.passed, tests)
- failed_tests = filter(lambda x: not x.passed, tests)
- try: percent = str(int(100.0*len(passed_tests) / len(tests)))
- except ZeroDivisionError: percent = 'n/a'
-
- tests_to_report = failed_tests
- if include_passed: tests_to_report = tests
-
- s=''
-
- s+= '\n'
- s+= '\nSystem info\n'
- s+= '\n
'
- s+= '
'+sysinfo+'
\n'
-
- s+= '\n'
- s+= '\nSTARTDATE: '+ startdate
- s+= '\nENDDATE: '+ enddate
- s+= '\nWIKI_ROOTPATH: '+ wiki_rootpath
- s+= '\nSYSID: '+sysid
- s+= '\nNUMTESTS: '+str(len(tests))
- s+= '\nNUMPASSED: '+str(len(passed_tests))
- s+= '\nPERCENTPASSED: '+ percent
- s+= '\n
'
-
- if not include_passed:
- s+= 'Failed tests:
\n'
-
- if len(tests_to_report)==0:
- s+= 'none
'
-
- for t in tests_to_report:
- if t.type in ('txt', 'ast', 'csg', 'term', 'echo'):
- s+='\n'+t.fullname+'
\n'
- s+=''+t.fulltestlog+'
\n\n'
- elif t.type=='png':
- tmp = t.actualfile.replace(builddir,'')
- wikiname_a = wikify_filename(tmp,wiki_rootpath,sysid)
- tmp = t.expectedfile.replace(os.path.dirname(builddir),'')
- wikiname_e = wikify_filename(tmp,wiki_rootpath,sysid)
- # imgtag_e = '
- # imatag_a = '
- imgtag_e = png_encode64( t.expectedfile, 250 )
- imgtag_a = png_encode64( t.actualfile, 250 )
- s+=''
- s+='\n'+t.fullname
- s+='\n |
Expected | Actual'
- s+='\n |
' + imgtag_e + ' | '
- s+='\n ' + imgtag_a + ' | '
- s+='\n
'
- s+='\n'
- s+=t.fulltestlog
- s+='\n
'
- else:
- raise Exception("Unknown test type %r"%t.type)
-
- s+='\n\n\n\n'
-
- s+= '
CMake .build files
\n'
- s+= '\n'
- makefiles_wikinames = {}
- for mf in sorted(makefiles.keys()):
- mfname = mf.strip().lstrip(os.path.sep)
- text = open(os.path.join(builddir,mfname)).read()
- s+= '
'+mfname+'
'
- s+= text
- tmp = mf.replace('CMakeFiles','').replace('.dir','')
- wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
- # s += '\n'+wikiname+'
'
- s+= '\n
'
- s+='\n'
-
- return head + s + tail
-
-def wiki_login(wikiurl,api_php_path,botname,botpass):
- site = mwclient.Site(wikiurl,api_php_path)
- site.login(botname,botpass)
- return site
-
-def wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikipgname):
- counter = 0
- done = False
- descrip = 'test'
- time.sleep(1)
- while not done:
- try:
- print 'login',botname,'to',wikiurl
- site = wiki_login(wikiurl,api_php_path,botname,botpass)
- print 'uploading...',
- if wikipgname.endswith('png'):
- site.upload(filedata,wikipgname,descrip,ignore=True)
- else:
- page = site.Pages[wikipgname]
- text = page.edit()
- page.save(filedata)
- done = True
- print 'transfer ok'
- except Exception, e:
- print 'Error:', type(e),e
- counter += 1
- if counter>maxretry:
- print 'giving up. please try a different wiki site'
- done = True
- else:
- print 'wiki',wikiurl,'down. retrying in 15 seconds'
- time.sleep(15)
-
-def upload(wikiurl,api_php_path='/',wiki_rootpath='test', sysid='null', botname='cakebaby',botpass='anniew',wikidir='.',dryrun=True):
- wetrun = not dryrun
- if dryrun: print 'dry run'
- try:
- global mwclient
- import mwclient
- except:
- print 'please download mwclient 0.6.5 and unpack here:', os.getcwd()
- sys.exit()
-
- if wetrun: site = wiki_login(wikiurl,api_php_path,botname,botpass)
-
- wikifiles = os.listdir(wikidir)
- testreport_page = filter( lambda x: 'test_report' in x, wikifiles )
- if (len(testreport_page)>1):
- print 'multiple test reports found, please clean dir',wikidir
- sys.exit()
-
- rootpage = testreport_page[0]
- print 'add',rootpage,' to main report page ',wiki_rootpath
- if wetrun:
- page = site.Pages[wiki_rootpath]
- text = page.edit()
- if not '[['+rootpage+']]' in text:
- page.save(text +'\n*[['+rootpage+']]\n')
-
- wikifiles = os.listdir(wikidir)
- wikifiles = filter(lambda x: not x.endswith('html'), wikifiles)
-
- print 'upload wiki pages:'
- for wikiname in wikifiles:
- filename = os.path.join(wikidir,wikiname)
- filedata = tryread(filename)
- print 'upload',len(filedata),'bytes from',wikiname
- if wetrun and len(filedata)>0:
- wiki_upload(wikiurl,api_php_path,botname,botpass,filedata,wikiname)
- if len(filedata)==0:
- print 'cancelling empty upload'
def findlogfile(builddir):
- logpath = os.path.join(builddir,'Testing','Temporary')
- logfilename = os.path.join(logpath,'LastTest.log.tmp')
- if not os.path.isfile(logfilename):
- logfilename = os.path.join(logpath,'LastTest.log')
- if not os.path.isfile(logfilename):
- print 'cant find and/or open logfile',logfilename
- sys.exit()
- return logpath, logfilename
+ logpath = os.path.join(builddir, 'Testing', 'Temporary')
+ logfilename = os.path.join(logpath, 'LastTest.log.tmp')
+ if not os.path.isfile(logfilename):
+ logfilename = os.path.join(logpath, 'LastTest.log')
+ if not os.path.isfile(logfilename):
+ print 'can\'t find and/or open logfile', logfilename
+ sys.exit()
+ return logfilename
+
+# --- Templating ---
+
+class Templates(object):
+ html_template = '''
+ Test run for {sysid}
+ {style}
+
+
+ {project_name} test run report
+
+ Sysid: {sysid}
+
+
+ Result summary: {numpassed} / {numtests} tests passed ({percent}%)
+
+
+ System info
+ {sysinfo}
+
+ start time: {startdate}
+ end time: {enddate}
+
+ Image tests
+ {image_tests}
+
+ Text tests
+ {text_tests}
+
+ build.make and flags.make
+ {makefiles}
+ '''
+
+ style = '''
+ '''
+
+ image_template = '''
+
+ {test_name} |
+ Expected image | Actual image |
+ {expected} | {actual} |
+
+
+
+
+ {test_log}
+
+ '''
+
+ text_template = '''
+ {test_name}
+
+
+ {test_log}
+
+ '''
+
+ makefile_template = '''
+ {name}
+
+ {text}
+
+ '''
+
+ def __init__(self, **defaults):
+ self.filled = {}
+ self.defaults = defaults
+
+ def fill(self, template, *args, **kwargs):
+ kwds = self.defaults.copy()
+ kwds.update(kwargs)
+ return getattr(self, template).format(*args, **kwds)
+
+ def add(self, template, var, *args, **kwargs):
+ self.filled[var] = self.filled.get(var, '') + self.fill(template, *args, **kwargs)
+ return self.filled[var]
+
+ def get(self, var):
+ return self.filled[var]
+
+
+def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles):
+ passed_tests = [test for test in tests if test.passed]
+ failed_tests = [test for test in tests if not test.passed]
+
+ report_tests = failed_tests
+ if include_passed:
+ report_tests = tests
+
+ percent = '%.0f' % (100.0 * len(passed_tests) / len(tests)) if tests else 'n/a'
+
+ templates = Templates()
+ for test in report_tests:
+ if test.type in ('txt', 'ast', 'csg', 'term', 'echo'):
+ templates.add('text_template', 'text_tests',
+ test_name=test.fullname,
+ test_log=test.fulltestlog)
+ elif test.type == 'png':
+ actual_img = png_encode64(test.actualfile,
+ data=vars(test).get('actualfile_data'))
+ expected_img = png_encode64(test.expectedfile,
+ data=vars(test).get('expectedfile_data'))
+ templates.add('image_template', 'image_tests',
+ test_name=test.fullname,
+ test_log=test.fulltestlog,
+ actual=actual_img,
+ expected=expected_img)
+ else:
+ raise TypeError('Unknown test type %r' % test.type)
+
+ for mf in sorted(makefiles.keys()):
+ mfname = mf.strip().lstrip(os.path.sep)
+ text = open(os.path.join(builddir, mfname)).read()
+ templates.add('makefile_template', 'makefiles', name=mfname, text=text)
+
+ text_tests = templates.get('text_tests')
+ image_tests = templates.get('image_tests')
+ makefiles_str = templates.get('makefiles')
+
+ return templates.fill('html_template', style=Templates.style,
+ sysid=sysid, sysinfo=sysinfo,
+ startdate=startdate, enddate=enddate,
+ project_name=project_name,
+ numtests=len(tests),
+ numpassed=len(passed_tests),
+ percent=percent, image_tests=image_tests,
+ text_tests=text_tests, makefiles=makefiles_str)
+
+# --- End Templating ---
+
+# --- Web Upload ---
+
+def postify(data):
+ return urlencode(data).encode()
+
+def create_page():
+ data = {
+ 'action': 'create',
+ 'type': 'html'
+ }
+ try:
+ response = urlopen('http://www.dinkypage.com', data=postify(data))
+ except:
+ return None
+ return response.geturl()
+
+def upload_html(page_url, title, html):
+ data = {
+ 'mode': 'editor',
+ 'title': title,
+ 'html': html,
+ 'ajax': '1'
+ }
+ try:
+ response = urlopen(page_url, data=postify(data))
+ except:
+ return False
+ return 'success' in response.read().decode()
+
+# --- End Web Upload ---
def debug(x):
- if debug_test_pp: print 'test_pretty_print: '+x
+ if debug_test_pp:
+ print 'test_pretty_print: ' + x
-debug_test_pp=False
-builddir=os.getcwd()
+debug_test_pp = False
+builddir = os.getcwd()
def main():
- #wikisite = 'cakebaby.referata.com'
- #wiki_api_path = ''
- global wikisite, wiki_api_path, wiki_rootpath, builddir, debug_test_pp
- global maxretry, dry, include_passed
+ global builddir, debug_test_pp
+ global maxretry, dry, include_passed
+ project_name = 'OpenSCAD'
+
+ if bool(os.getenv("TEST_GENERATE")):
+ sys.exit(0)
+
+ # --- Command Line Parsing ---
+
+ if '--debug' in ' '.join(sys.argv):
+ debug_test_pp = True
+ maxretry = 10
- wikisite = 'cakebaby.wikia.com'
- wiki_api_path = '/'
- wiki_rootpath = 'OpenSCAD'
- if '--debug' in string.join(sys.argv): debug_test_pp=True
- maxretry = 10
+ include_passed = False
+ if '--include-passed' in sys.argv:
+ include_passed = True
- if bool(os.getenv("TEST_GENERATE")): sys.exit(0)
+ dry = False
+ debug('running test_pretty_print')
+ if '--dryrun' in sys.argv:
+ dry = True
- include_passed = False
- if '--include-passed' in sys.argv: include_passed = True
+ suffix = ezsearch('--suffix=(.*?) ', ' '.join(sys.argv) + ' ')
+ builddir = ezsearch('--builddir=(.*?) ', ' '.join(sys.argv) + ' ')
+ if not builddir:
+ builddir = os.getcwd()
+ debug('build dir set to ' + builddir)
+
+ # --- End Command Line Parsing ---
- dry = False
- debug( 'running test_pretty_print' )
- if '--dryrun' in sys.argv: dry=True
- suffix = ezsearch('--suffix=(.*?) ',string.join(sys.argv)+' ')
- builddir = ezsearch('--builddir=(.*?) ',string.join(sys.argv)+' ')
- if builddir=='': builddir=os.getcwd()
- debug( 'build dir set to ' + builddir )
+ sysinfo, sysid = read_sysinfo(os.path.join(builddir, 'sysinfo.txt'))
+ makefiles = load_makefiles(builddir)
+ logfilename = findlogfile(builddir)
+ testlog = tryread(logfilename)
+ startdate, tests, enddate = parselog(testlog)
+ if debug_test_pp:
+ print 'found sysinfo.txt,',
+ print 'found', len(makefiles),'makefiles,',
+ print 'found', len(tests),'test results'
- sysinfo, sysid = read_sysinfo(os.path.join(builddir,'sysinfo.txt'))
- makefiles = load_makefiles(builddir)
- logpath, logfilename = findlogfile(builddir)
- testlog = tryread(logfilename)
- startdate, tests, enddate = parselog(testlog)
- if debug_test_pp:
- print 'found sysinfo.txt,',
- print 'found', len(makefiles),'makefiles,',
- print 'found', len(tests),'test results'
+ html = to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles)
+ html_basename = sysid + '_report.html'
+ html_filename = os.path.join(builddir, 'Testing', 'Temporary', html_basename)
+ debug('saving ' + html_filename + ' ' + str(len(htmldata)) + ' bytes')
+ trysave(html_filename, html)
- imgs, txtpages = towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
+ page_url = create_page()
+ if upload_html(page_url, title='OpenSCAD test results', html=html):
+ share_url = page_url.partition('?')[0]
+ print 'html report uploaded at', share_url
+ else:
+ print 'could not upload html report'
- wikidir = os.path.join(logpath,sysid+'_report')
- debug( 'erasing files in ' + wikidir )
- try: map(lambda x:os.remove(os.path.join(wikidir,x)), os.listdir(wikidir))
- except: pass
- debug( 'output dir:\n' + wikidir.replace(os.getcwd(),'') )
- debug( 'writing ' + str(len(imgs)) + ' images' )
- debug( 'writing ' + str(len(txtpages)-1) + ' text pages' )
- debug( 'writing index.html ' )
- if '--wiki' in string.join(sys.argv):
- print "wiki output is deprecated"
- for pgname in sorted(imgs): trysave( os.path.join(wikidir,pgname), imgs[pgname])
- for pgname in sorted(txtpages): trysave( os.path.join(wikidir,pgname), txtpages[pgname])
-
- htmldata = tohtml(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
- html_basename = sysid+'_report.html'
- html_filename = os.path.join(builddir,'Testing','Temporary',html_basename)
- debug('saving ' +html_filename + ' ' + str(len(htmldata)) + ' bytes')
- trysave( html_filename, htmldata )
- print "report saved:", html_filename.replace(os.getcwd()+os.path.sep,'')
-
- if '--wiki-upload' in sys.argv:
- print "wiki upload is deprecated."
- upload(wikisite,wiki_api_path,wiki_rootpath,sysid,'openscadbot',
- 'tobdacsnepo',wikidir,dryrun=dry)
- print 'upload attempt complete'
-
- debug( 'test_pretty_print complete' )
+ debug('test_pretty_print complete')
if __name__=='__main__':
- main()
+ main()
From 8d3365a79ab7afd287545da3f8b2ba12f0c43585 Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sat, 7 Dec 2013 14:50:49 -0800
Subject: [PATCH 31/66] Update design philosopy comments
---
tests/test_pretty_print.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index c26b919d..44df156f 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -17,11 +17,9 @@
# Design philosophy
#
# 1. parse the data (images, logs) into easy-to-use data structures
-# 2. wikifiy the data
-# 3. save the wikified data to disk
-# 4. generate html, including base64 encoding of images
-# 5. save html file
-# 6. upload html to public site and share with others
+# 2. generate html, including base64 encoding of images
+# 3. save html file
+# 4. upload html to public site and share with others
# todo
#
From 8a21092dc01dfe54550b8fceada35999bed3056b Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sat, 7 Dec 2013 17:59:07 -0800
Subject: [PATCH 32/66] Adding imports
---
tests/test_pretty_print.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index 44df156f..c2deab8c 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -33,6 +33,13 @@ import hashlib
import subprocess
import time
import platform
+try:
+ from urllib.request import urlopen
+ from urllib.parse import urlencode
+except ImportError:
+ from urllib2 import urlopen
+ from urllib import urlencode
+
def tryread(filename):
data = None
From b131464f954b2d10f6b065b45038652af2379742 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Sun, 8 Dec 2013 13:50:03 -0500
Subject: [PATCH 33/66] #559 CMAKE_OSX_DEPLOYMENT_TARGET needs to be cached
---
tests/CMakeLists.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b84775b8..f92eddf5 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -14,14 +14,14 @@ include(CMakeParseArguments.cmake)
# Detect Lion and force gcc
IF (APPLE)
- # Somehow, since we build dependencies for 10.7, we need to also build executables
- # for 10.7. This used to not be necessary, but since 10.9 it apparently is..
- SET(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
EXECUTE_PROCESS(COMMAND sw_vers -productVersion OUTPUT_VARIABLE MACOSX_VERSION)
IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.9.0")
message("Detected Maverick (10.9) or later")
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
+ # Somehow, since we build dependencies for 10.7, we need to also build executables
+ # for 10.7. This used to not be necessary, but since 10.9 it apparently is..
+ SET(CMAKE_OSX_DEPLOYMENT_TARGET 10.7 CACHE STRING "Deployment target")
ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0")
message("Detected Mountain Lion (10.8)")
set(CMAKE_C_COMPILER "clang")
From cb2b094b269646c79f48f525306b74cd7bc0f633 Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sun, 8 Dec 2013 10:51:25 -0800
Subject: [PATCH 34/66] Fixed small error in Templates.get
---
tests/test_pretty_print.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index c2deab8c..349a730a 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -304,10 +304,10 @@ class Templates(object):
def add(self, template, var, *args, **kwargs):
self.filled[var] = self.filled.get(var, '') + self.fill(template, *args, **kwargs)
- return self.filled[var]
+ return self.get(var)
def get(self, var):
- return self.filled[var]
+ return self.filled.get(var, '')
def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles):
From 8971c67fa209fe29ead54034cd0f9be39c0909ff Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sun, 8 Dec 2013 11:18:58 -0800
Subject: [PATCH 35/66] Fixed variable name error
---
tests/test_pretty_print.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index 349a730a..359165e8 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -441,7 +441,7 @@ def main():
html = to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles)
html_basename = sysid + '_report.html'
html_filename = os.path.join(builddir, 'Testing', 'Temporary', html_basename)
- debug('saving ' + html_filename + ' ' + str(len(htmldata)) + ' bytes')
+ debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes')
trysave(html_filename, html)
page_url = create_page()
From a22ebd608d941b5ae1767a2903f4bacc925840ed Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sun, 8 Dec 2013 11:28:19 -0800
Subject: [PATCH 36/66] Moved include_passed
---
tests/test_pretty_print.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index 359165e8..c3035c55 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -395,6 +395,7 @@ def debug(x):
print 'test_pretty_print: ' + x
debug_test_pp = False
+include_passed = False
builddir = os.getcwd()
def main():
@@ -411,7 +412,6 @@ def main():
debug_test_pp = True
maxretry = 10
- include_passed = False
if '--include-passed' in sys.argv:
include_passed = True
From e27f971d9c3dc26a9a023a2aea4c265dac53437b Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sun, 8 Dec 2013 12:15:04 -0800
Subject: [PATCH 37/66] Changed upload code to use gists
---
tests/test_pretty_print.py | 90 +++++++++++++++++++++++---------------
1 file changed, 54 insertions(+), 36 deletions(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index c3035c55..a88a22ea 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -17,9 +17,11 @@
# Design philosophy
#
# 1. parse the data (images, logs) into easy-to-use data structures
-# 2. generate html, including base64 encoding of images
-# 3. save html file
-# 4. upload html to public site and share with others
+# 2. wikifiy the data
+# 3. save the wikified data to disk
+# 4. generate html, including base64 encoding of images
+# 5. save html file
+# 6. upload html to public site and share with others
# todo
#
@@ -34,12 +36,12 @@ import subprocess
import time
import platform
try:
- from urllib.request import urlopen
- from urllib.parse import urlencode
-except ImportError:
- from urllib2 import urlopen
- from urllib import urlencode
-
+ from urllib.request import urlopen, Request
+except:
+ from urllib2 import urlopen, Request
+import json
+import base64
+
def tryread(filename):
data = None
@@ -304,7 +306,7 @@ class Templates(object):
def add(self, template, var, *args, **kwargs):
self.filled[var] = self.filled.get(var, '') + self.fill(template, *args, **kwargs)
- return self.get(var)
+ return self.filled[var]
def get(self, var):
return self.filled.get(var, '')
@@ -361,32 +363,46 @@ def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles):
# --- Web Upload ---
-def postify(data):
- return urlencode(data).encode()
+API_URL = 'https://api.github.com/%s'
+# Username is personal access token, from https://github.com/settings/applications
+# This way, no password is needed
+USERNAME = 'b2af28787fb1efd9a5b3a3b4f1be8a3ac9b5b335'
+PASSWORD = ''
-def create_page():
- data = {
- 'action': 'create',
- 'type': 'html'
- }
+def make_auth(username, password):
+ auth = '%s:%s' % (USERNAME, PASSWORD)
+ return base64.b64encode(auth.encode())
+
+def post_gist(name, content):
+ gist = '''{
+ "description": "",
+ "public": true,
+ "files": {
+ "%s": {
+ "content": "%s"
+ }
+ }
+ }'''
+ gist = gist % (name, content)
+
+ req = Request(API_URL % 'gists')
+ req.add_header('Authorization', b'Basic ' + make_auth(USERNAME, PASSWORD))
try:
- response = urlopen('http://www.dinkypage.com', data=postify(data))
+ result = urlopen(req, data=gist)
except:
+ print 'Could not upload results'
return None
- return response.geturl()
+ return json.loads(result.read())
-def upload_html(page_url, title, html):
- data = {
- 'mode': 'editor',
- 'title': title,
- 'html': html,
- 'ajax': '1'
- }
- try:
- response = urlopen(page_url, data=postify(data))
- except:
- return False
- return 'success' in response.read().decode()
+
+def get_raw_urls(result):
+ files = result.get('files', {})
+ for file in files:
+ yield files[file].get('raw_url').replace('gist.github.com', 'rawgithub.com')
+
+result = post_gist('aaabbb.html', '''I\'m asdf
''')
+for url in get_raw_urls(result):
+ print(url)
# --- End Web Upload ---
@@ -438,18 +454,20 @@ def main():
print 'found', len(makefiles),'makefiles,',
print 'found', len(tests),'test results'
+
html = to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles)
html_basename = sysid + '_report.html'
html_filename = os.path.join(builddir, 'Testing', 'Temporary', html_basename)
debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes')
trysave(html_filename, html)
- page_url = create_page()
- if upload_html(page_url, title='OpenSCAD test results', html=html):
- share_url = page_url.partition('?')[0]
- print 'html report uploaded at', share_url
- else:
+ result = post_gist(name=html_basename, content=html)
+ if result is None:
print 'could not upload html report'
+ return
+
+ for url in get_raw_urls(result):
+ print 'html report uploaded at', url
debug('test_pretty_print complete')
From 88cc7edafd47ec167609c05eb22c3f8eb642d225 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Sun, 8 Dec 2013 15:16:58 -0500
Subject: [PATCH 38/66] #559 Fix Qt font rendering on OS X 10.9
---
patches/qt4/patch-qeventdispatcher.diff | 86 +++++++++++++++++++++++++
patches/qt4/patch-qfontdatabase.diff | 29 +++++++++
2 files changed, 115 insertions(+)
create mode 100644 patches/qt4/patch-qeventdispatcher.diff
create mode 100644 patches/qt4/patch-qfontdatabase.diff
diff --git a/patches/qt4/patch-qeventdispatcher.diff b/patches/qt4/patch-qeventdispatcher.diff
new file mode 100644
index 00000000..89ed4788
--- /dev/null
+++ b/patches/qt4/patch-qeventdispatcher.diff
@@ -0,0 +1,86 @@
+--- src/gui/kernel/qeventdispatcher_mac_p.h 2013-06-07 01:16:59.000000000 -0400
++++ src/gui/kernel/qeventdispatcher_mac_p_new-8184b49c12d887928921ed5b695c8c6f04a07514.h 2013-12-08 14:31:01.000000000 -0500
+@@ -173,6 +173,7 @@
+ #ifdef QT_MAC_USE_COCOA
+ // The following variables help organizing modal sessions:
+ static QStack cocoaModalSessionStack;
++ static QStack cocoaModalSessionStackPendingEnd;
+ static bool currentExecIsNSAppRun;
+ static bool nsAppRunCalledByQt;
+ static bool cleanupModalSessionsNeeded;
+@@ -180,6 +181,7 @@
+ static NSModalSession currentModalSession();
+ static void updateChildrenWorksWhenModal();
+ static void temporarilyStopAllModalSessions();
++ static void stopAllPendingEndModalSessions();
+ static void beginModalSession(QWidget *widget);
+ static void endModalSession(QWidget *widget);
+ static void cancelWaitForMoreEvents();
+--- src/gui/kernel/qeventdispatcher_mac.mm 2013-06-07 01:16:59.000000000 -0400
++++ src/gui/kernel/qeventdispatcher_mac_new-833e02de99494686f8dd7a567f6e19e847508f11.mm 2013-12-08 14:30:59.000000000 -0500
+@@ -603,6 +603,9 @@
+ while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
+ qt_mac_waitForMoreModalSessionEvents();
+
++ // stop all pending end modal sessions
++ d->stopAllPendingEndModalSessions();
++
+ if (!d->interrupt && session == d->currentModalSessionCached) {
+ // Someone called [NSApp stopModal:] from outside the event
+ // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
+@@ -678,6 +681,9 @@
+ if (!d->interrupt)
+ QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+
++ // stop all pending end modal sessions
++ d->stopAllPendingEndModalSessions();
++
+ // Since the window that holds modality might have changed while processing
+ // events, we we need to interrupt when we return back the previous process
+ // event recursion to ensure that we spin the correct modal session.
+@@ -781,6 +787,7 @@
+
+ #ifdef QT_MAC_USE_COCOA
+ QStack QEventDispatcherMacPrivate::cocoaModalSessionStack;
++QStack QEventDispatcherMacPrivate::cocoaModalSessionStackPendingEnd;
+ bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false;
+ bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false;
+ bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false;
+@@ -828,6 +835,20 @@
+ currentModalSessionCached = 0;
+ }
+
++void QEventDispatcherMacPrivate::stopAllPendingEndModalSessions()
++{
++ // stop all modal sessions pending end
++ int stackSize = cocoaModalSessionStackPendingEnd.size();
++ for (int i=stackSize-1; i>=0; --i) {
++ QCocoaModalSessionInfo &info = cocoaModalSessionStackPendingEnd[i];
++ cocoaModalSessionStackPendingEnd.remove(i);
++ if (info.session) {
++ [NSApp endModalSession:info.session];
++ [(NSWindow *)info.nswindow release];
++ }
++ }
++}
++
+ NSModalSession QEventDispatcherMacPrivate::currentModalSession()
+ {
+ // If we have one or more modal windows, this function will create
+@@ -925,10 +946,12 @@
+ }
+ cocoaModalSessionStack.remove(i);
+ currentModalSessionCached = 0;
+- if (info.session) {
+- [NSApp endModalSession:info.session];
+- [(NSWindow *)info.nswindow release];
+- }
++
++ // Cannot stop the sessions here since we might still be inside a
++ // [NSApp runModalSession:] call. Add the session to the pending end stack and
++ // process the stack after the call to [NSApp runModalSession:] returns.
++ if (info.session)
++ cocoaModalSessionStackPendingEnd.push(info);
+ }
+
+ updateChildrenWorksWhenModal();
diff --git a/patches/qt4/patch-qfontdatabase.diff b/patches/qt4/patch-qfontdatabase.diff
new file mode 100644
index 00000000..c0788906
--- /dev/null
+++ b/patches/qt4/patch-qfontdatabase.diff
@@ -0,0 +1,29 @@
+--- src/gui/text/qfontdatabase.cpp 2013-06-07 01:16:59.000000000 -0400
++++ src/gui/text/qfontdatabase_new-bb2beddc3ae55c4676d190d0ac99aa32d322a6a5.cpp 2013-12-08 14:51:10.000000000 -0500
+@@ -441,6 +441,7 @@
+ #endif
+ #if !defined(QWS) && defined(Q_OS_MAC)
+ bool fixedPitchComputed : 1;
++ QString postscriptName;
+ #endif
+ #ifdef Q_WS_X11
+ bool symbol_checked : 1;
+--- src/gui/text/qfontdatabase_mac.cpp 2013-06-07 01:16:59.000000000 -0400
++++ src/gui/text/qfontdatabase_mac_new-41f29865db84152efb41c048470f713353a0a84c.cpp 2013-12-08 14:51:05.000000000 -0500
+@@ -147,6 +147,7 @@
+ QCFString family_name = (CFStringRef)CTFontDescriptorCopyLocalizedAttribute(font, kCTFontFamilyNameAttribute, NULL);
+ QCFString style_name = (CFStringRef)CTFontDescriptorCopyLocalizedAttribute(font, kCTFontStyleNameAttribute, NULL);
+ QtFontFamily *family = db->family(family_name, true);
++ family->postscriptName = QCFString((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontNameAttribute));
+
+ if (QCFType languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) {
+ CFIndex length = CFArrayGetCount(languages);
+@@ -327,7 +328,7 @@
+ if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
+ QByteArray family_name = db->families[k]->name.toUtf8();
+ #if defined(QT_MAC_USE_COCOA)
+- QCFType ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL);
++ QCFType ctFont = CTFontCreateWithName(QCFString(db->families[k]->postscriptName), 12, NULL);
+ if (ctFont) {
+ fontName = CTFontCopyFullName(ctFont);
+ goto found;
From eb046015d2fd4fa3e4e3c8844cd6dc8f4d3eca99 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Sun, 8 Dec 2013 15:17:17 -0500
Subject: [PATCH 39/66] #559 Fix Qt font rendering on OS X 10.9
---
src/openscad.cc | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/openscad.cc b/src/openscad.cc
index ece68189..ab842357 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -474,6 +474,13 @@ bool QtUseGUI()
int gui(vector &inputFiles, const fs::path &original_path, int argc, char ** argv)
{
+#ifdef Q_OS_MACX
+ if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
+ // fix Mac OS X 10.9 (mavericks) font issue
+ // https://bugreports.qt-project.org/browse/QTBUG-32789
+ QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
+ }
+#endif
QApplication app(argc, argv, true); //useGUI);
#ifdef Q_WS_MAC
app.installEventFilter(new EventFilter(&app));
From d6bffc4691cf1467cf93d527724f7278b418273d Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sun, 8 Dec 2013 12:17:19 -0800
Subject: [PATCH 40/66] Taking out test accidentially left in, my user token
---
tests/test_pretty_print.py | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index a88a22ea..b601c842 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -366,7 +366,7 @@ def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles):
API_URL = 'https://api.github.com/%s'
# Username is personal access token, from https://github.com/settings/applications
# This way, no password is needed
-USERNAME = 'b2af28787fb1efd9a5b3a3b4f1be8a3ac9b5b335'
+USERNAME = '' # add OpenScad user token
PASSWORD = ''
def make_auth(username, password):
@@ -400,9 +400,6 @@ def get_raw_urls(result):
for file in files:
yield files[file].get('raw_url').replace('gist.github.com', 'rawgithub.com')
-result = post_gist('aaabbb.html', '''I\'m asdf
''')
-for url in get_raw_urls(result):
- print(url)
# --- End Web Upload ---
From 7075d8d9c4dde62798022bdfe02c5d57e997ba43 Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Thu, 5 Dec 2013 15:56:50 +1100
Subject: [PATCH 41/66] Fix for bad boost libraries
Get this error because of a search for a non-existent library on linux64
-----------
[ 69%] Built target tests-cgal
Scanning dependencies of target cgalcachetest
[ 70%] Building CXX object
CMakeFiles/cgalcachetest.dir/cgalcachetest.cc.o
make[2]: *** No rule to make target `/usr/lib/libboost_thread.so',
needed by `cgalcachetest'. Stop.
make[1]: *** [CMakeFiles/cgalcachetest.dir/all] Error 2
make: *** [all] Error 2
[2]+ Done gedit openscad.pro (wd:
~/git/openscad_unicode)
----------
---
tests/CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index f92eddf5..d7ad18a4 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -384,11 +384,45 @@ if("${CGAL_MAJOR_VERSION}.${CGAL_MINOR_VERSION}" VERSION_LESS 3.6)
endif()
inclusion(CGAL_DIR CGAL_INCLUDE_DIRS)
+#Get rid of bad libraries suggested for BOOST dependencies (they don't exist on some machines and cause build failures).
+#/usr/lib/libboost_thread.so;/usr/lib/libboost_system.so;
+string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_system.so" FIND_POSITION )
+if(NOT "-1" STREQUAL ${FIND_POSITION} )
+if(NOT EXISTS "/usr/lib/libboost_system.so")
+ MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_system.so -- stripping" )
+ string(REPLACE "/usr/lib/libboost_system.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+endif()
+endif()
+string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_thread.so" FIND_POSITION )
+if(NOT "-1" STREQUAL ${FIND_POSITION} )
+if(NOT EXISTS "/usr/lib/libboost_thread.so")
+ MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_thread.so -- stripping" )
+ string(REPLACE "/usr/lib/libboost_thread.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+endif()
+endif()
if(${CMAKE_CXX_COMPILER} MATCHES ".*clang.*" AND NOT ${CGAL_CXX_FLAGS_INIT} STREQUAL "" )
string(REPLACE "-frounding-math" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
string(REPLACE "--param=ssp-buffer-size=4" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
endif()
+if (NOT $ENV{OPENSCAD_LIBRARIES} STREQUAL "")
+ # Force pkg-config to look _only_ in the local library folder
+ # in case OPENSCAD_LIBRARIES is set.
+ set(ENV{PKG_CONFIG_PATH} "$ENV{OPENSCAD_LIBRARIES}/lib/pkgconfig")
+ set(ENV{PKG_CONFIG_LIBDIR} "$ENV{OPENSCAD_LIBRARIES}/lib/pkgconfig")
+endif()
+
+# Find libraries (system installed or dependency built) using pkg-config
+find_package(PkgConfig REQUIRED)
+
+#GLib-2
+pkg_search_module(GLIB2 REQUIRED glib-2.0>=2.2.0)
+#Can't use the CXXFlags directly as they are ;-separated
+string(REPLACE ";" " " GLIB2_CFLAGS "${GLIB2_CFLAGS}")
+message(STATUS "glib-2.0 found: ${GLIB2_VERSION}")
+
+add_definitions(${GLIB2_CFLAGS})
+
# Imagemagick
if (SKIP_IMAGEMAGICK)
From 3abf64249fd667f0b7f558ecfbbf35cfe9916a5d Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Thu, 5 Dec 2013 17:56:54 +1100
Subject: [PATCH 42/66] Unicode support for strings
Add suport for using unicode strings in .scad files. Support iterating
across them/accessing them via [] and searching.
--------
Add GLIB (to build for test and normal build -- both with installed and
built locally development files).
Add support for unicode chars to length and search builtin functions and
[] for strings.
Added unicode testing functions.
Ad GLIB to library info page.
---
.gitignore | 1 +
README.md | 1 +
common.pri | 1 +
glib-2.0.pri | 38 ++++++
openscad.pro | 3 +-
scripts/check-dependencies.sh | 17 ++-
scripts/uni-build-dependencies.sh | 1 +
scripts/uni-get-dependencies.sh | 15 +--
src/AboutDialog.html | 1 +
src/PlatformUtils.cc | 3 +
src/func.cc | 49 ++++++--
src/value.cc | 18 ++-
testdata/scad/misc/search-tests-unicode.scad | 116 ++++++++++++++++++
testdata/scad/misc/string-unicode.scad | 36 ++++++
tests/CMakeLists.txt | 6 +-
.../search-tests-unicode-expected.echo | 109 ++++++++++++++++
.../echotest/string-unicode-expected.echo | 104 ++++++++++++++++
17 files changed, 497 insertions(+), 22 deletions(-)
create mode 100644 glib-2.0.pri
create mode 100644 testdata/scad/misc/search-tests-unicode.scad
create mode 100644 testdata/scad/misc/string-unicode.scad
create mode 100644 tests/regression/echotest/search-tests-unicode-expected.echo
create mode 100644 tests/regression/echotest/string-unicode-expected.echo
diff --git a/.gitignore b/.gitignore
index 50dace12..59bac496 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
/*.scad
*.dmg
+*~
*.tar*
Makefile
objects
diff --git a/README.md b/README.md
index 27f12cec..1e97e0f6 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,7 @@ Follow the instructions for the platform you're compiling on below.
* [OpenCSG (1.3.2)](http://www.opencsg.org/)
* [GLEW (1.5.4 ->)](http://glew.sourceforge.net/)
* [Eigen (3.0 - 3.2)](http://eigen.tuxfamily.org/)
+* [glib2 (2.2.0)](https://developer.gnome.org/glib/)
* [GCC C++ Compiler (4.2 ->)](http://gcc.gnu.org/)
* [Bison (2.4)](http://www.gnu.org/software/bison/)
* [Flex (2.5.35)](http://flex.sourceforge.net/)
diff --git a/common.pri b/common.pri
index 7153ded7..696c8b1d 100644
--- a/common.pri
+++ b/common.pri
@@ -11,3 +11,4 @@ include(opencsg.pri)
include(glew.pri)
include(eigen.pri)
include(boost.pri)
+include(glib-2.0.pri)
\ No newline at end of file
diff --git a/glib-2.0.pri b/glib-2.0.pri
new file mode 100644
index 00000000..0fbc4e2d
--- /dev/null
+++ b/glib-2.0.pri
@@ -0,0 +1,38 @@
+# Detect glib-2.0, then use this priority list to determine
+# which library to use:
+#
+# Priority
+# 1. GLIB2_INCLUDEPATH / GLIB2_LIBPATH (qmake parameter, not checked it given on commandline)
+# 2. OPENSCAD_LIBRARIES (environment variable)
+# 3. system's standard include paths from pkg-config
+
+glib-2.0 {
+
+# read environment variables
+OPENSCAD_LIBRARIES_DIR = $$(OPENSCAD_LIBRARIES)
+GLIB2_DIR = $$(GLIB2DIR)
+
+!isEmpty(OPENSCAD_LIBRARIES_DIR) {
+ isEmpty(GLIB2_INCLUDEPATH) {
+ GLIB2_INCLUDEPATH_1 = $$OPENSCAD_LIBRARIES_DIR/include/glib-2.0
+ GLIB2_INCLUDEPATH_2 = $$OPENSCAD_LIBRARIES_DIR/lib/glib-2.0/include
+ GLIB2_LIBPATH = $$OPENSCAD_LIBRARIES_DIR/lib
+ }
+}
+
+isEmpty(GLIB2_INCLUDEPATH) {
+ GLIB2_CFLAGS = $$system("pkg-config --cflags glib-2.0")
+} else {
+ GLIB2_CFLAGS = -I$$GLIB2_INCLUDEPATH_1
+ GLIB2_CFLAGS += -I$$GLIB2_INCLUDEPATH_2
+}
+
+isEmpty(GLIB2_LIBPATH) {
+ GLIB2_LIBS = $$system("pkg-config --libs glib-2.0")
+} else {
+ GLIB2_LIBS = -L$$GLIB2_LIBPATH -lglib-2.0
+}
+
+QMAKE_CXXFLAGS += $$GLIB2_CFLAGS
+LIBS += $$GLIB2_LIBS
+}
diff --git a/openscad.pro b/openscad.pro
index b38419ef..ec5af20c 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -8,7 +8,7 @@
# OPENCSGDIR
# OPENSCAD_LIBRARIES
#
-# Please see the 'Buildling' sections of the OpenSCAD user manual
+# Please see the 'Building' sections of the OpenSCAD user manual
# for updated tips & workarounds.
#
# http://en.wikibooks.org/wiki/OpenSCAD_User_Manual
@@ -156,6 +156,7 @@ CONFIG += cgal
CONFIG += opencsg
CONFIG += boost
CONFIG += eigen
+CONFIG += glib-2.0
#Uncomment the following line to enable QCodeEdit
#CONFIG += qcodeedit
diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh
index b63c6770..e5871989 100755
--- a/scripts/check-dependencies.sh
+++ b/scripts/check-dependencies.sh
@@ -66,6 +66,21 @@ cgal_sysver()
cgal_sysver_result=`grep "define *CGAL_VERSION *[0-9.]*" $cgalpath | awk '{print $3}'`
}
+glib2_sysver()
+{
+ #Get architecture triplet - e.g. x86_64-linux-gnu
+ glib2archtriplet=`gcc -dumpmachine 2>/dev/null`
+ if [ -z "$VAR" ]; then
+ glib2archtriplet=`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`
+ fi
+ glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h
+ if [ ! -e $glib2path ]; then return; fi
+ glib2major=`grep "define *GLIB_MAJOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
+ glib2minor=`grep "define *GLIB_MINOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
+ glib2micro=`grep "define *GLIB_MICRO_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
+ glib2_sysver_result="${glib2major}.${glib2minor}.${glib2micro}"
+}
+
boost_sysver()
{
boostpath=$1/include/boost/version.hpp
@@ -530,7 +545,7 @@ checkargs()
main()
{
- deps="qt4 cgal gmp mpfr boost opencsg glew eigen gcc bison flex make"
+ deps="qt4 cgal gmp mpfr boost opencsg glew eigen glib2 gcc bison flex make"
#deps="$deps curl git" # not technically necessary for build
#deps="$deps python cmake imagemagick" # only needed for tests
#deps="cgal"
diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh
index e652c473..8d912c35 100755
--- a/scripts/uni-build-dependencies.sh
+++ b/scripts/uni-build-dependencies.sh
@@ -603,5 +603,6 @@ build_boost 1.53.0
build_cgal 4.0.2
build_glew 1.9.0
build_opencsg 1.3.2
+build_glib2 2.38.2
echo "OpenSCAD dependencies built and installed to " $BASEDIR
diff --git a/scripts/uni-get-dependencies.sh b/scripts/uni-get-dependencies.sh
index a0306ef8..d2408c00 100755
--- a/scripts/uni-get-dependencies.sh
+++ b/scripts/uni-get-dependencies.sh
@@ -8,7 +8,7 @@ get_fedora_deps()
{
sudo yum install qt-devel bison flex eigen3-devel python-paramiko \
boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \
- opencsg-devel git libXmu-devel curl imagemagick ImageMagick make \
+ opencsg-devel git libXmu-devel curl imagemagick ImageMagick glib2-devel make \
xorg-x11-server-Xvfb
}
@@ -21,7 +21,7 @@ get_altlinux_deps()
{
for i in boost-devel boost-filesystem-devel gcc4.5 gcc4.5-c++ boost-program_options-devel \
boost-thread-devel boost-system-devel boost-regex-devel eigen3 libmpfr libgmp libgmp_cxx-devel qt4-devel libcgal-devel git-core \
- libglew-devel flex bison curl imagemagick; do sudo apt-get install $i; done
+ libglew-devel flex bison curl imagemagick glib2-devel; do sudo apt-get install $i; done
}
get_freebsd_deps()
@@ -29,20 +29,21 @@ get_freebsd_deps()
pkg_add -r bison boost-libs cmake git bash eigen3 flex gmake gmp mpfr \
xorg libGLU libXmu libXi xorg-vfbserver glew \
qt4-corelib qt4-gui qt4-moc qt4-opengl qt4-qmake qt4-rcc qt4-uic \
- opencsg cgal curl imagemagick
+ opencsg cgal curl imagemagick glib2-devel
}
get_netbsd_deps()
{
sudo pkgin install bison boost cmake git bash eigen flex gmake gmp mpfr \
qt4 glew cgal opencsg modular-xorg python27 py27-paramiko curl \
- imagemagick ImageMagick
+ imagemagick ImageMagick glib2-devel
}
get_opensuse_deps()
{
sudo zypper install libeigen3-devel mpfr-devel gmp-devel boost-devel \
- libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl
+ libqt4-devel glew-devel cmake git bison flex cgal-devel opencsg-devel curl \
+ glib2-devel
}
get_mageia_deps()
@@ -50,7 +51,7 @@ get_mageia_deps()
sudo urpmi ctags
sudo urpmi task-c-devel task-c++-devel libqt4-devel libgmp-devel \
libmpfr-devel libboost-devel eigen3-devel libglew-devel bison flex \
- cmake imagemagick python curl git x11-server-xvfb
+ cmake imagemagick glib2-devel python curl git x11-server-xvfb
}
get_debian_deps()
@@ -59,7 +60,7 @@ get_debian_deps()
libxmu-dev cmake bison flex git-core libboost-all-dev \
libXi-dev libmpfr-dev libboost-dev libglew-dev \
libeigen3-dev libcgal-dev libopencsg-dev libgmp3-dev libgmp-dev \
- python-paramiko curl imagemagick; do
+ python-paramiko curl imagemagick libglib2.0-dev; do
sudo apt-get -y install $pkg;
done
}
diff --git a/src/AboutDialog.html b/src/AboutDialog.html
index 99e7c3b2..65a54d71 100644
--- a/src/AboutDialog.html
+++ b/src/AboutDialog.html
@@ -64,6 +64,7 @@ Please visit this link for a copy of the license: C++, GCC, clang
python
Nullsoft installer
+GLib
diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc
index b02b822e..8b39f6df 100644
--- a/src/PlatformUtils.cc
+++ b/src/PlatformUtils.cc
@@ -1,6 +1,8 @@
#include "PlatformUtils.h"
#include "boosty.h"
+#include
+
bool PlatformUtils::createLibraryPath()
{
std::string path = PlatformUtils::libraryPath();
@@ -114,6 +116,7 @@ std::string PlatformUtils::info()
<< "\nOpenCSG version: " << OPENCSG_VERSION_STRING
<< "\nQt version: " << qtVersion
<< "\nMingW build: " << mingwstatus
+ << "\nGLib version: " << GLIB_MAJOR_VERSION << "." << GLIB_MINOR_VERSION << "." << GLIB_MICRO_VERSION
<< "\nOPENSCADPATH: " << getenv("OPENSCADPATH") << "\n"
;
return s.str();
diff --git a/src/func.cc b/src/func.cc
index 865a2b42..4587f725 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -45,6 +45,8 @@
#include
#include
+/*Unicode support for string lengths and array accesses*/
+#include
#ifdef __WIN32__
#include
@@ -306,7 +308,11 @@ Value builtin_length(const Context *, const EvalContext *evalctx)
{
if (evalctx->numArgs() == 1) {
if (evalctx->getArgValue(0).type() == Value::VECTOR) return Value(int(evalctx->getArgValue(0).toVector().size()));
- if (evalctx->getArgValue(0).type() == Value::STRING) return Value(int(evalctx->getArgValue(0).toString().size()));
+ if (evalctx->getArgValue(0).type() == Value::STRING) {
+ //Unicode glyph count for the length -- rather than the string (num. of bytes) length.
+ std::string text = evalctx->getArgValue(0).toString();
+ return Value(int( g_utf8_strlen( text.c_str(), text.size() ) ));
+ }
}
return Value();
}
@@ -380,10 +386,17 @@ Value builtin_lookup(const Context *, const EvalContext *evalctx)
num_returns_per_match : int;
index_col_num : int;
+ The search string and searched strings can be unicode strings.
Examples:
Index values return as list:
search("a","abcdabcd");
- - returns [0,4]
+ - returns [0]
+ search("Л","Л"); //A unicode string
+ - returns [0]
+ search("🂡aЛ","a🂡Л🂡a🂡Л🂡a",0);
+ - returns [[1,3,5,7],[0,4,8],[2,6]]
+ search("a","abcdabcd",0); //Search up to all matches
+ - returns [[0,4]]
search("a","abcdabcd",1);
- returns [0]
search("e","abcdabcd",1);
@@ -433,16 +446,25 @@ Value builtin_search(const Context *, const EvalContext *evalctx)
}
} else if (findThis.type() == Value::STRING) {
unsigned int searchTableSize;
- if (searchTable.type() == Value::STRING) searchTableSize = searchTable.toString().size();
- else searchTableSize = searchTable.toVector().size();
- for (size_t i = 0; i < findThis.toString().size(); i++) {
+ //Unicode glyph count for the length
+ unsigned int findThisSize = g_utf8_strlen( findThis.toString().c_str(), findThis.toString().size() );
+ if (searchTable.type() == Value::STRING) {
+ searchTableSize = g_utf8_strlen( searchTable.toString().c_str(), searchTable.toString().size() );
+ } else {
+ searchTableSize = searchTable.toVector().size();
+ }
+ for (size_t i = 0; i < findThisSize; i++) {
unsigned int matchCount = 0;
Value::VectorType resultvec;
for (size_t j = 0; j < searchTableSize; j++) {
- if ((searchTable.type() == Value::VECTOR &&
- findThis.toString()[i] == searchTable.toVector()[j].toVector()[index_col_num].toString()[0]) ||
- (searchTable.type() == Value::STRING &&
- findThis.toString()[i] == searchTable.toString()[j])) {
+ gchar* ptr_ft = g_utf8_offset_to_pointer(findThis.toString().c_str(), i);
+ gchar* ptr_st = NULL;
+ if(searchTable.type() == Value::VECTOR) {
+ ptr_st = g_utf8_offset_to_pointer(searchTable.toVector()[j].toVector()[index_col_num].toString().c_str(), 0);
+ } else if(searchTable.type() == Value::STRING){
+ ptr_st = g_utf8_offset_to_pointer(searchTable.toString().c_str(), j);
+ }
+ if( (ptr_ft) && (ptr_st) && (g_utf8_get_char(ptr_ft) == g_utf8_get_char(ptr_st)) ) {
Value resultValue((double(j)));
matchCount++;
if (num_returns_per_match == 1) {
@@ -454,7 +476,14 @@ Value builtin_search(const Context *, const EvalContext *evalctx)
if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) break;
}
}
- if (matchCount == 0) PRINTB(" WARNING: search term not found: \"%s\"", findThis.toString()[i]);
+ if (matchCount == 0) {
+ gchar* ptr_ft = g_utf8_offset_to_pointer(findThis.toString().c_str(), i);
+ gchar utf8_of_cp[6] = ""; //A buffer for a single unicode character to be copied into
+ if(ptr_ft) {
+ g_utf8_strncpy( utf8_of_cp, ptr_ft, 1 );
+ }
+ PRINTB(" WARNING: search term not found: \"%s\"", utf8_of_cp );
+ }
if (num_returns_per_match == 0 || num_returns_per_match > 1) {
returnvec.push_back(Value(resultvec));
}
diff --git a/src/value.cc b/src/value.cc
index 5afb650c..c8a88c69 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -36,6 +36,8 @@
#include
#include "boost-utils.h"
#include "boosty.h"
+/*Unicode support for string lengths and array accesses*/
+#include
std::ostream &operator<<(std::ostream &stream, const Filename &filename)
{
@@ -579,14 +581,28 @@ Value Value::operator-() const
}
*/
+/*
+ * bracket operation [] detecting multi-byte unicode.
+ * If the string is multi-byte unicode then the index will offset to the character (2 or 4 byte) and not to the byte.
+ * A 'normal' string with byte chars are a subset of unicode and still work.
+ */
class bracket_visitor : public boost::static_visitor
{
public:
Value operator()(const std::string &str, const double &idx) const {
int i = int(idx);
Value v;
+ //Check that the index is positive and less than the size in bytes
if ((i >= 0) && (i < (int)str.size())) {
- v = Value(str[int(idx)]);
+ //Ensure character (not byte) index is inside the character/glyph array
+ if( (unsigned) i < g_utf8_strlen( str.c_str(), str.size() ) ) {
+ gchar utf8_of_cp[6] = ""; //A buffer for a single unicode character to be copied into
+ gchar* ptr = g_utf8_offset_to_pointer(str.c_str(), i);
+ if(ptr) {
+ g_utf8_strncpy(utf8_of_cp, ptr, 1);
+ }
+ v = std::string(utf8_of_cp);
+ }
// std::cout << "bracket_visitor: " << v << "\n";
}
return v;
diff --git a/testdata/scad/misc/search-tests-unicode.scad b/testdata/scad/misc/search-tests-unicode.scad
new file mode 100644
index 00000000..d863eff9
--- /dev/null
+++ b/testdata/scad/misc/search-tests-unicode.scad
@@ -0,0 +1,116 @@
+//Test search with unicode strings
+
+//Helper function that pretty prints our search test
+//Expected result is checked against execution of a search() invocation and OK/FAIL is indicated
+module test_search_and_echo( exp_res, search_to_find, search_to_search, search_up_to_num_matches = undef)
+{
+ if(undef != search_up_to_num_matches)
+ {
+ assign( test_res = search(search_to_find, search_to_search, search_up_to_num_matches) )
+ echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ", ", search_up_to_num_matches, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" ));
+ }
+ else
+ {
+ assign( test_res = search(search_to_find, search_to_search) )
+ echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" ));
+ }
+}
+
+
+//"Normal" text for comparison
+echo ("----- Lookup of 1 byte into 1 byte");
+//Hits - up_to_count 1
+test_search_and_echo( [0], "a","aaaa" );
+test_search_and_echo( [0], "a","aaaa",1 );
+test_search_and_echo( [0,0], "aa","aaaa" );
+test_search_and_echo( [0,0], "aa","aaaa",1 );
+
+
+//Hits - up to count 1+ (incl 0 == all)
+test_search_and_echo( [[0,1,2,3]] , "a","aaaa",0 );
+test_search_and_echo( [[0,1]], "a","aaaa",2 );
+test_search_and_echo( [[0,1,2]], "a","aaaa",3 );
+test_search_and_echo( [[0,1,2,3]] , "a","aaaa",4 );
+test_search_and_echo( [[0,1,2,3],[0,1,2,3]] , "aa","aaaa",0 );
+//Misses
+test_search_and_echo( [], "b","aaaa" );
+test_search_and_echo( [], "b","aaaa",1 );
+test_search_and_echo( [[]], "b","aaaa",0 );
+test_search_and_echo( [[]], "b","aaaa",2 );
+
+test_search_and_echo( [], "bb","aaaa" );
+test_search_and_echo( [], "bb","aaaa",1 );
+test_search_and_echo( [[],[]], "bb","aaaa",0 );
+test_search_and_echo( [[],[]], "bb","aaaa",2 );
+//Miss - empties
+test_search_and_echo( [], "","aaaa" );
+test_search_and_echo( [], "","" );
+test_search_and_echo( [], "a","" );
+
+
+//Unicode tests
+echo ("----- Lookup of multi-byte into 1 byte");
+test_search_and_echo( [], "Л","aaaa" );
+test_search_and_echo( [], "🂡","aaaa" );
+test_search_and_echo( [[]], "Л","aaaa",0 );
+test_search_and_echo( [[]], "🂡","aaaa",0 );
+
+test_search_and_echo( [], "ЛЛ","aaaa" );
+test_search_and_echo( [], "🂡🂡","aaaa" );
+test_search_and_echo( [[],[]], "ЛЛ","aaaa",0 );
+test_search_and_echo( [[],[]], "🂡🂡","aaaa",0 );
+
+echo ("----- Lookup of 1-byte into multi-byte");
+test_search_and_echo( [] , "a","ЛЛЛЛ" );
+test_search_and_echo( [] , "a","🂡🂡🂡🂡" );
+test_search_and_echo( [] , "a","ЛЛЛЛ",1 );
+
+test_search_and_echo( [[]] , "a","🂡🂡🂡🂡",0 );
+test_search_and_echo( [[]] , "a","🂡🂡🂡🂡",2 );
+
+echo ("----- Lookup of 1-byte into mixed multi-byte");
+test_search_and_echo( [0], "a","aЛaЛaЛaЛa" );
+test_search_and_echo( [0], "a","a🂡a🂡a🂡a🂡a" );
+test_search_and_echo( [0], "a","a🂡Л🂡a🂡Л🂡a" );
+
+test_search_and_echo( [[0,2,4,6,8]], "a","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[0,2,4,6,8]], "a","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[0,4,8]] , "a","a🂡Л🂡a🂡Л🂡a", 0 );
+
+echo ("----- Lookup of 2-byte into 2-byte");
+test_search_and_echo( [0] , "Л","ЛЛЛЛ" );
+test_search_and_echo( [[0,1,2,3]] , "Л","ЛЛЛЛ",0 );
+
+echo ("----- Lookup of 2-byte into 4-byte");
+test_search_and_echo( [] , "Л","🂡🂡🂡🂡" );
+
+echo ("----- Lookup of 4-byte into 4-byte");
+test_search_and_echo( [0] , "🂡","🂡🂡🂡🂡" );
+test_search_and_echo( [[0,1,2,3]], "🂡","🂡🂡🂡🂡",0 );
+
+echo ("----- Lookup of 4-byte into 2-byte");
+test_search_and_echo( [] , "🂡","ЛЛЛЛ" );
+
+echo ("----- Lookup of 2-byte into mixed multi-byte");
+test_search_and_echo( [1] , "Л","aЛaЛaЛaЛa",1 );
+test_search_and_echo( [] , "Л","a🂡a🂡a🂡a🂡a", 1 );
+test_search_and_echo( [2] , "Л","a🂡Л🂡a🂡Л🂡a", 1 );
+
+test_search_and_echo( [[1,3,5,7]] , "Л","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[]] , "Л","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[2,6]] , "Л","a🂡Л🂡a🂡Л🂡a", 0 );
+
+echo ("----- Lookup of 4-byte into mixed multi-byte");
+test_search_and_echo( [] , "🂡","aЛaЛaЛaЛa",1 );
+test_search_and_echo( [1] , "🂡","a🂡a🂡a🂡a🂡a", 1 );
+
+test_search_and_echo( [[]] , "🂡","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[1,3,5,7]] , "🂡","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[1,3,5,7]] , "🂡","a🂡Л🂡a🂡Л🂡a", 0 );
+
+echo ("----- Lookup of mixed multi-byte into mixed multi-byte");
+test_search_and_echo( [[0,2,4,6,8],[1,3,5,7],[]], "aЛ🂡","aЛaЛaЛaЛa",0 );
+test_search_and_echo( [[0,2,4,6,8],[],[1,3,5,7]], "aЛ🂡","a🂡a🂡a🂡a🂡a", 0 );
+test_search_and_echo( [[0,4,8],[2,6],[1,3,5,7]] , "aЛ🂡","a🂡Л🂡a🂡Л🂡a", 0 );
+test_search_and_echo( [[1,3,5,7],[0,4,8],[2,6]] , "🂡aЛ","a🂡Л🂡a🂡Л🂡a", 0 );
+
diff --git a/testdata/scad/misc/string-unicode.scad b/testdata/scad/misc/string-unicode.scad
new file mode 100644
index 00000000..d8e3e5c9
--- /dev/null
+++ b/testdata/scad/misc/string-unicode.scad
@@ -0,0 +1,36 @@
+//Test how well arrays of unicode string are accessed.
+
+texts_array = [
+"DEADBEEF",
+"Ленивый рыжий кот",
+"كسول الزنجبيل القط",
+"懶惰的姜貓",
+"äöü ÄÖÜ ß",
+"😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐",
+"⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏",
+"🂡🂱🃁🃑",
+];
+
+text_2bytes = "Ленивый рыжий кот";
+text_4bytes = "🂡🂱🃁🃑";
+
+
+//Test all the normal accesses
+for (text_array_idx = [0:(len(texts_array)-1)])
+{
+ echo( "[", text_array_idx, "] = ", texts_array[text_array_idx], " of len=", len(texts_array[text_array_idx]), ":" );
+ for (text_idx = [0:(len(texts_array[text_array_idx])-1)])
+ {
+ echo( " [", text_idx, ,"]=", texts_array[text_array_idx][text_idx] );
+ }
+}
+
+//Test one past the last element of (x-byte unicode). This will be one past the length but inside the char length of the string
+echo( "Past end of unicode only 2-byte ", text_2bytes[len(text_2bytes)] );
+echo( "Past end of unicode only 4-byte ", text_4bytes[len(text_4bytes)] );
+
+//Test past the last element of (x-byte unicode). Outside both lengths.
+echo( "Past end of both 2-byte ", text_2bytes[ len(text_2bytes) * 2 ] );
+echo( "Past end of both 4-byte ", text_4bytes[ len(text_4bytes) * 4 ] );
+
+
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d7ad18a4..3c19b77c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -594,8 +594,8 @@ set(OFFSCREEN_SOURCES
../src/OpenCSGRenderer.cc)
add_library(tests-core STATIC ${CORE_SOURCES})
-target_link_libraries(tests-core ${OPENGL_LIBRARIES})
-set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${Boost_LIBRARIES})
+target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} )
+set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} )
add_library(tests-common STATIC ${COMMON_SOURCES})
target_link_libraries(tests-common tests-core)
@@ -815,8 +815,10 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/dim-all.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-indexing.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-unicode.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/vector-values.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests-unicode.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad
diff --git a/tests/regression/echotest/search-tests-unicode-expected.echo b/tests/regression/echotest/search-tests-unicode-expected.echo
new file mode 100644
index 00000000..801bc8c8
--- /dev/null
+++ b/tests/regression/echotest/search-tests-unicode-expected.echo
@@ -0,0 +1,109 @@
+ECHO: "----- Lookup of 1 byte into 1 byte"
+ECHO: "Expect [0] for search(a, aaaa)=[0]. OK"
+ECHO: "Expect [0] for search(a, aaaa, 1)=[0]. OK"
+ECHO: "Expect [0, 0] for search(aa, aaaa)=[0, 0]. OK"
+ECHO: "Expect [0, 0] for search(aa, aaaa, 1)=[0, 0]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(a, aaaa, 0)=[[0, 1, 2, 3]]. OK"
+ECHO: "Expect [[0, 1]] for search(a, aaaa, 2)=[[0, 1]]. OK"
+ECHO: "Expect [[0, 1, 2]] for search(a, aaaa, 3)=[[0, 1, 2]]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(a, aaaa, 4)=[[0, 1, 2, 3]]. OK"
+ECHO: "Expect [[0, 1, 2, 3], [0, 1, 2, 3]] for search(aa, aaaa, 0)=[[0, 1, 2, 3], [0, 1, 2, 3]]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(b, aaaa)=[]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(b, aaaa, 1)=[]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[]] for search(b, aaaa, 0)=[[]]. OK"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[]] for search(b, aaaa, 2)=[[]]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(bb, aaaa)=[]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [] for search(bb, aaaa, 1)=[]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[], []] for search(bb, aaaa, 0)=[[], []]. OK"
+ WARNING: search term not found: "b"
+ WARNING: search term not found: "b"
+ECHO: "Expect [[], []] for search(bb, aaaa, 2)=[[], []]. OK"
+ECHO: "Expect [] for search(, aaaa)=[]. OK"
+ECHO: "Expect [] for search(, )=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, )=[]. OK"
+ECHO: "----- Lookup of multi-byte into 1 byte"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(Л, aaaa)=[]. OK"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡, aaaa)=[]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[]] for search(Л, aaaa, 0)=[[]]. OK"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[]] for search(🂡, aaaa, 0)=[[]]. OK"
+ WARNING: search term not found: "Л"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(ЛЛ, aaaa)=[]. OK"
+ WARNING: search term not found: "🂡"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡🂡, aaaa)=[]. OK"
+ WARNING: search term not found: "Л"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[], []] for search(ЛЛ, aaaa, 0)=[[], []]. OK"
+ WARNING: search term not found: "🂡"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[], []] for search(🂡🂡, aaaa, 0)=[[], []]. OK"
+ECHO: "----- Lookup of 1-byte into multi-byte"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, ЛЛЛЛ)=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, 🂡🂡🂡🂡)=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [] for search(a, ЛЛЛЛ, 1)=[]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [[]] for search(a, 🂡🂡🂡🂡, 0)=[[]]. OK"
+ WARNING: search term not found: "a"
+ECHO: "Expect [[]] for search(a, 🂡🂡🂡🂡, 2)=[[]]. OK"
+ECHO: "----- Lookup of 1-byte into mixed multi-byte"
+ECHO: "Expect [0] for search(a, aЛaЛaЛaЛa)=[0]. OK"
+ECHO: "Expect [0] for search(a, a🂡a🂡a🂡a🂡a)=[0]. OK"
+ECHO: "Expect [0] for search(a, a🂡Л🂡a🂡Л🂡a)=[0]. OK"
+ECHO: "Expect [[0, 2, 4, 6, 8]] for search(a, aЛaЛaЛaЛa, 0)=[[0, 2, 4, 6, 8]]. OK"
+ECHO: "Expect [[0, 2, 4, 6, 8]] for search(a, a🂡a🂡a🂡a🂡a, 0)=[[0, 2, 4, 6, 8]]. OK"
+ECHO: "Expect [[0, 4, 8]] for search(a, a🂡Л🂡a🂡Л🂡a, 0)=[[0, 4, 8]]. OK"
+ECHO: "----- Lookup of 2-byte into 2-byte"
+ECHO: "Expect [0] for search(Л, ЛЛЛЛ)=[0]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(Л, ЛЛЛЛ, 0)=[[0, 1, 2, 3]]. OK"
+ECHO: "----- Lookup of 2-byte into 4-byte"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(Л, 🂡🂡🂡🂡)=[]. OK"
+ECHO: "----- Lookup of 4-byte into 4-byte"
+ECHO: "Expect [0] for search(🂡, 🂡🂡🂡🂡)=[0]. OK"
+ECHO: "Expect [[0, 1, 2, 3]] for search(🂡, 🂡🂡🂡🂡, 0)=[[0, 1, 2, 3]]. OK"
+ECHO: "----- Lookup of 4-byte into 2-byte"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡, ЛЛЛЛ)=[]. OK"
+ECHO: "----- Lookup of 2-byte into mixed multi-byte"
+ECHO: "Expect [1] for search(Л, aЛaЛaЛaЛa, 1)=[1]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [] for search(Л, a🂡a🂡a🂡a🂡a, 1)=[]. OK"
+ECHO: "Expect [2] for search(Л, a🂡Л🂡a🂡Л🂡a, 1)=[2]. OK"
+ECHO: "Expect [[1, 3, 5, 7]] for search(Л, aЛaЛaЛaЛa, 0)=[[1, 3, 5, 7]]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[]] for search(Л, a🂡a🂡a🂡a🂡a, 0)=[[]]. OK"
+ECHO: "Expect [[2, 6]] for search(Л, a🂡Л🂡a🂡Л🂡a, 0)=[[2, 6]]. OK"
+ECHO: "----- Lookup of 4-byte into mixed multi-byte"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [] for search(🂡, aЛaЛaЛaЛa, 1)=[]. OK"
+ECHO: "Expect [1] for search(🂡, a🂡a🂡a🂡a🂡a, 1)=[1]. OK"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[]] for search(🂡, aЛaЛaЛaЛa, 0)=[[]]. OK"
+ECHO: "Expect [[1, 3, 5, 7]] for search(🂡, a🂡a🂡a🂡a🂡a, 0)=[[1, 3, 5, 7]]. OK"
+ECHO: "Expect [[1, 3, 5, 7]] for search(🂡, a🂡Л🂡a🂡Л🂡a, 0)=[[1, 3, 5, 7]]. OK"
+ECHO: "----- Lookup of mixed multi-byte into mixed multi-byte"
+ WARNING: search term not found: "🂡"
+ECHO: "Expect [[0, 2, 4, 6, 8], [1, 3, 5, 7], []] for search(aЛ🂡, aЛaЛaЛaЛa, 0)=[[0, 2, 4, 6, 8], [1, 3, 5, 7], []]. OK"
+ WARNING: search term not found: "Л"
+ECHO: "Expect [[0, 2, 4, 6, 8], [], [1, 3, 5, 7]] for search(aЛ🂡, a🂡a🂡a🂡a🂡a, 0)=[[0, 2, 4, 6, 8], [], [1, 3, 5, 7]]. OK"
+ECHO: "Expect [[0, 4, 8], [2, 6], [1, 3, 5, 7]] for search(aЛ🂡, a🂡Л🂡a🂡Л🂡a, 0)=[[0, 4, 8], [2, 6], [1, 3, 5, 7]]. OK"
+ECHO: "Expect [[1, 3, 5, 7], [0, 4, 8], [2, 6]] for search(🂡aЛ, a🂡Л🂡a🂡Л🂡a, 0)=[[1, 3, 5, 7], [0, 4, 8], [2, 6]]. OK"
diff --git a/tests/regression/echotest/string-unicode-expected.echo b/tests/regression/echotest/string-unicode-expected.echo
new file mode 100644
index 00000000..b4b848fd
--- /dev/null
+++ b/tests/regression/echotest/string-unicode-expected.echo
@@ -0,0 +1,104 @@
+ECHO: "[", 0, "] = ", "DEADBEEF", " of len=", 8, ":"
+ECHO: " [", 0, "]=", "D"
+ECHO: " [", 1, "]=", "E"
+ECHO: " [", 2, "]=", "A"
+ECHO: " [", 3, "]=", "D"
+ECHO: " [", 4, "]=", "B"
+ECHO: " [", 5, "]=", "E"
+ECHO: " [", 6, "]=", "E"
+ECHO: " [", 7, "]=", "F"
+ECHO: "[", 1, "] = ", "Ленивый рыжий кот", " of len=", 17, ":"
+ECHO: " [", 0, "]=", "Л"
+ECHO: " [", 1, "]=", "е"
+ECHO: " [", 2, "]=", "н"
+ECHO: " [", 3, "]=", "и"
+ECHO: " [", 4, "]=", "в"
+ECHO: " [", 5, "]=", "ы"
+ECHO: " [", 6, "]=", "й"
+ECHO: " [", 7, "]=", " "
+ECHO: " [", 8, "]=", "р"
+ECHO: " [", 9, "]=", "ы"
+ECHO: " [", 10, "]=", "ж"
+ECHO: " [", 11, "]=", "и"
+ECHO: " [", 12, "]=", "й"
+ECHO: " [", 13, "]=", " "
+ECHO: " [", 14, "]=", "к"
+ECHO: " [", 15, "]=", "о"
+ECHO: " [", 16, "]=", "т"
+ECHO: "[", 2, "] = ", "كسول الزنجبيل القط", " of len=", 18, ":"
+ECHO: " [", 0, "]=", "ك"
+ECHO: " [", 1, "]=", "س"
+ECHO: " [", 2, "]=", "و"
+ECHO: " [", 3, "]=", "ل"
+ECHO: " [", 4, "]=", " "
+ECHO: " [", 5, "]=", "ا"
+ECHO: " [", 6, "]=", "ل"
+ECHO: " [", 7, "]=", "ز"
+ECHO: " [", 8, "]=", "ن"
+ECHO: " [", 9, "]=", "ج"
+ECHO: " [", 10, "]=", "ب"
+ECHO: " [", 11, "]=", "ي"
+ECHO: " [", 12, "]=", "ل"
+ECHO: " [", 13, "]=", " "
+ECHO: " [", 14, "]=", "ا"
+ECHO: " [", 15, "]=", "ل"
+ECHO: " [", 16, "]=", "ق"
+ECHO: " [", 17, "]=", "ط"
+ECHO: "[", 3, "] = ", "懶惰的姜貓", " of len=", 5, ":"
+ECHO: " [", 0, "]=", "懶"
+ECHO: " [", 1, "]=", "惰"
+ECHO: " [", 2, "]=", "的"
+ECHO: " [", 3, "]=", "姜"
+ECHO: " [", 4, "]=", "貓"
+ECHO: "[", 4, "] = ", "äöü ÄÖÜ ß", " of len=", 9, ":"
+ECHO: " [", 0, "]=", "ä"
+ECHO: " [", 1, "]=", "ö"
+ECHO: " [", 2, "]=", "ü"
+ECHO: " [", 3, "]=", " "
+ECHO: " [", 4, "]=", "Ä"
+ECHO: " [", 5, "]=", "Ö"
+ECHO: " [", 6, "]=", "Ü"
+ECHO: " [", 7, "]=", " "
+ECHO: " [", 8, "]=", "ß"
+ECHO: "[", 5, "] = ", "😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐", " of len=", 16, ":"
+ECHO: " [", 0, "]=", "😁"
+ECHO: " [", 1, "]=", "😂"
+ECHO: " [", 2, "]=", "😃"
+ECHO: " [", 3, "]=", "😄"
+ECHO: " [", 4, "]=", "😅"
+ECHO: " [", 5, "]=", "😆"
+ECHO: " [", 6, "]=", "😇"
+ECHO: " [", 7, "]=", "😈"
+ECHO: " [", 8, "]=", "😉"
+ECHO: " [", 9, "]=", "😊"
+ECHO: " [", 10, "]=", "😋"
+ECHO: " [", 11, "]=", "😌"
+ECHO: " [", 12, "]=", "😍"
+ECHO: " [", 13, "]=", "😎"
+ECHO: " [", 14, "]=", "😏"
+ECHO: " [", 15, "]=", "😐"
+ECHO: "[", 6, "] = ", "⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏", " of len=", 15, ":"
+ECHO: " [", 0, "]=", "⠁"
+ECHO: " [", 1, "]=", "⠂"
+ECHO: " [", 2, "]=", "⠃"
+ECHO: " [", 3, "]=", "⠄"
+ECHO: " [", 4, "]=", "⠅"
+ECHO: " [", 5, "]=", "⠆"
+ECHO: " [", 6, "]=", "⠇"
+ECHO: " [", 7, "]=", "⠈"
+ECHO: " [", 8, "]=", "⠉"
+ECHO: " [", 9, "]=", "⠊"
+ECHO: " [", 10, "]=", "⠋"
+ECHO: " [", 11, "]=", "⠌"
+ECHO: " [", 12, "]=", "⠍"
+ECHO: " [", 13, "]=", "⠎"
+ECHO: " [", 14, "]=", "⠏"
+ECHO: "[", 7, "] = ", "🂡🂱🃁🃑", " of len=", 4, ":"
+ECHO: " [", 0, "]=", "🂡"
+ECHO: " [", 1, "]=", "🂱"
+ECHO: " [", 2, "]=", "🃁"
+ECHO: " [", 3, "]=", "🃑"
+ECHO: "Past end of unicode only 2-byte ", undef
+ECHO: "Past end of unicode only 4-byte ", undef
+ECHO: "Past end of both 2-byte ", undef
+ECHO: "Past end of both 4-byte ", undef
From c0849eb1d3c98db505eec0396c56276a9a35120f Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Fri, 6 Dec 2013 09:56:22 +1100
Subject: [PATCH 43/66] Update comments/messages for CGAL source of bad boost
libraries
Add comments and change to a status instead of a warning (as we recover
nicely and it is a known issue in CGAL @
https://bugs.launchpad.net/ubuntu/+source/cgal/+bug/1242111)
---
tests/CMakeLists.txt | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 3c19b77c..2533103e 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -383,23 +383,24 @@ if("${CGAL_MAJOR_VERSION}.${CGAL_MINOR_VERSION}" VERSION_LESS 3.6)
message(FATAL_ERROR "CGAL >= 3.6 required")
endif()
inclusion(CGAL_DIR CGAL_INCLUDE_DIRS)
-
-#Get rid of bad libraries suggested for BOOST dependencies (they don't exist on some machines and cause build failures).
-#/usr/lib/libboost_thread.so;/usr/lib/libboost_system.so;
+#Remove bad BOOST libraries from CGAL 3rd party dependencies when they don't exist (such as on 64-bit Ubuntu 13.10).
+#Libs of concern are /usr/lib/libboost_thread.so;/usr/lib/libboost_system.so;
+#Confirmed bug in CGAL @ https://bugs.launchpad.net/ubuntu/+source/cgal/+bug/1242111
string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_system.so" FIND_POSITION )
if(NOT "-1" STREQUAL ${FIND_POSITION} )
-if(NOT EXISTS "/usr/lib/libboost_system.so")
- MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_system.so -- stripping" )
- string(REPLACE "/usr/lib/libboost_system.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
-endif()
+ if(NOT EXISTS "/usr/lib/libboost_system.so")
+ MESSAGE( STATUS "CGAL_3RD_PARTY_LIBRARIES:Removing non-existent /usr/lib/libboost_system.so" )
+ string(REPLACE "/usr/lib/libboost_system.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+ endif()
endif()
string(FIND "${CGAL_3RD_PARTY_LIBRARIES}" "/usr/lib/libboost_thread.so" FIND_POSITION )
if(NOT "-1" STREQUAL ${FIND_POSITION} )
-if(NOT EXISTS "/usr/lib/libboost_thread.so")
- MESSAGE( WARNING "CGAL_3RD_PARTY_LIBRARIES:Found erroneous /usr/lib/libboost_thread.so -- stripping" )
- string(REPLACE "/usr/lib/libboost_thread.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
-endif()
+ if(NOT EXISTS "/usr/lib/libboost_thread.so")
+ MESSAGE( STATUS "CGAL_3RD_PARTY_LIBRARIES:Removing non-existent /usr/lib/libboost_thread.so" )
+ string(REPLACE "/usr/lib/libboost_thread.so" "" CGAL_3RD_PARTY_LIBRARIES ${CGAL_3RD_PARTY_LIBRARIES})
+ endif()
endif()
+
if(${CMAKE_CXX_COMPILER} MATCHES ".*clang.*" AND NOT ${CGAL_CXX_FLAGS_INIT} STREQUAL "" )
string(REPLACE "-frounding-math" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
string(REPLACE "--param=ssp-buffer-size=4" "" CGAL_CXX_FLAGS_INIT ${CGAL_CXX_FLAGS_INIT})
From d2575016b989b9cee5b44c29f10a76d84a7bf182 Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Fri, 6 Dec 2013 17:46:52 +1100
Subject: [PATCH 44/66] Add in missed glib build dependencies for OS X and unix
---
scripts/macosx-build-dependencies.sh | 25 +++++++++++++++++++++++++
scripts/uni-build-dependencies.sh | 25 +++++++++++++++++++++++++
2 files changed, 50 insertions(+)
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index d4ca1f7c..5e26feee 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -285,6 +285,30 @@ build_glew()
make GLEW_DEST=$DEPLOYDIR CC=$CC CFLAGS.EXTRA="-no-cpp-precomp -dynamic -fno-common -mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" LDFLAGS.EXTRA="-mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" STRIP= install
}
+build_glib2()
+{
+ version="$1"
+ maj_min_version="${version%.*}" #Drop micro
+
+ if [ -e $DEPLOYDIR/lib/glib-2.0 ]; then
+ echo "glib2 already installed. not building"
+ return
+ fi
+
+ echo "Building glib2 $version..."
+ cd "$BASEDIR"/src
+ rm -rf "glib-$version"
+ if [ ! -f "glib-$version.tar.xz" ]; then
+ curl --insecure -LO "http://ftp.gnome.org/pub/gnome/sources/glib/$maj_min_version/glib-$version.tar.xz"
+ fi
+ tar xJf "glib-$version.tar.xz"
+ cd "glib-$version"
+
+ ./configure --prefix="$DEPLOYDIR"
+ make -j$NUMCPU
+ make install
+}
+
build_opencsg()
{
version=$1
@@ -449,6 +473,7 @@ build_boost 1.54.0
# NB! For CGAL, also update the actual download URL in the function
build_cgal 4.3
build_glew 1.10.0
+build_glib2 2.38.1
build_opencsg 1.3.2
if $OPTION_DEPLOY; then
# build_sparkle andymatuschak 0ed83cf9f2eeb425d4fdd141c01a29d843970c20
diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh
index 8d912c35..ba328b7e 100755
--- a/scripts/uni-build-dependencies.sh
+++ b/scripts/uni-build-dependencies.sh
@@ -409,6 +409,31 @@ build_glew()
GLEW_DEST=$DEPLOYDIR $MAKER install
}
+build_glib2()
+{
+ version="$1"
+ maj_min_version="${version%.*}" #Drop micro
+
+ if [ -e $DEPLOYDIR/lib/glib-2.0 ]; then
+echo "glib2 already installed. not building"
+ return
+fi
+
+echo "Building glib2 $version..."
+ cd "$BASEDIR"/src
+ rm -rf "glib-$version"
+ if [ ! -f "glib-$version.tar.xz" ]; then
+curl --insecure -LO "http://ftp.gnome.org/pub/gnome/sources/glib/$maj_min_version/glib-$version.tar.xz"
+ fi
+tar xJf "glib-$version.tar.xz"
+ cd "glib-$version"
+
+ ./configure --prefix="$DEPLOYDIR"
+ make -j$NUMCPU
+ make install
+
+}
+
build_opencsg()
{
if [ -e $DEPLOYDIR/lib/libopencsg.so ]; then
From d46ce3fb8150fe01c0c07fac11ea2e9a8ed97038 Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Fri, 6 Dec 2013 18:33:42 +1100
Subject: [PATCH 45/66] Add specific tests for unicode len()
---
testdata/scad/misc/string-unicode.scad | 10 +++++++++-
tests/regression/echotest/string-unicode-expected.echo | 3 +++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/testdata/scad/misc/string-unicode.scad b/testdata/scad/misc/string-unicode.scad
index d8e3e5c9..1386d63d 100644
--- a/testdata/scad/misc/string-unicode.scad
+++ b/testdata/scad/misc/string-unicode.scad
@@ -1,3 +1,12 @@
+//Test length reporting
+text_1bytes_len = "1234";
+text_2bytes_len = "ЛЛЛЛ";
+text_4bytes_len = "🂡🂱🃁🃑";
+
+echo( "text_1bytes_len = ", text_1bytes_len, " len = ", len(text_1bytes_len) );
+echo( "text_2bytes_len = ", text_2bytes_len, " len = ", len(text_2bytes_len) );
+echo( "text_4bytes_len = ", text_4bytes_len, " len = ", len(text_4bytes_len) );
+
//Test how well arrays of unicode string are accessed.
texts_array = [
@@ -33,4 +42,3 @@ echo( "Past end of unicode only 4-byte ", text_4bytes[len(text_4bytes)] );
echo( "Past end of both 2-byte ", text_2bytes[ len(text_2bytes) * 2 ] );
echo( "Past end of both 4-byte ", text_4bytes[ len(text_4bytes) * 4 ] );
-
diff --git a/tests/regression/echotest/string-unicode-expected.echo b/tests/regression/echotest/string-unicode-expected.echo
index b4b848fd..a1cd3bec 100644
--- a/tests/regression/echotest/string-unicode-expected.echo
+++ b/tests/regression/echotest/string-unicode-expected.echo
@@ -1,3 +1,6 @@
+ECHO: "text_1bytes_len = ", "1234", " len = ", 4
+ECHO: "text_2bytes_len = ", "ЛЛЛЛ", " len = ", 4
+ECHO: "text_4bytes_len = ", "🂡🂱🃁🃑", " len = ", 4
ECHO: "[", 0, "] = ", "DEADBEEF", " of len=", 8, ":"
ECHO: " [", 0, "]=", "D"
ECHO: " [", 1, "]=", "E"
From b7c818bf00b5c9e23ee97ce6669eb66a9b404562 Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sun, 8 Dec 2013 16:15:50 -0800
Subject: [PATCH 46/66] Revert to uploading to dinkypage, gists won't work
---
tests/test_pretty_print.py | 73 ++++++++++++++++----------------------
1 file changed, 30 insertions(+), 43 deletions(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index b601c842..675867e7 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -36,12 +36,11 @@ import subprocess
import time
import platform
try:
- from urllib.request import urlopen, Request
+ from urllib.request import urlopen
+ from urllib.parse import urlencode
except:
- from urllib2 import urlopen, Request
-import json
-import base64
-
+ from urllib2 import urlopen
+ from urllib import urlencode
def tryread(filename):
data = None
@@ -363,43 +362,32 @@ def to_html(project_name, startdate, tests, enddate, sysinfo, sysid, makefiles):
# --- Web Upload ---
-API_URL = 'https://api.github.com/%s'
-# Username is personal access token, from https://github.com/settings/applications
-# This way, no password is needed
-USERNAME = '' # add OpenScad user token
-PASSWORD = ''
+def postify(data):
+ return urlencode(data).encode()
-def make_auth(username, password):
- auth = '%s:%s' % (USERNAME, PASSWORD)
- return base64.b64encode(auth.encode())
-
-def post_gist(name, content):
- gist = '''{
- "description": "",
- "public": true,
- "files": {
- "%s": {
- "content": "%s"
- }
- }
- }'''
- gist = gist % (name, content)
-
- req = Request(API_URL % 'gists')
- req.add_header('Authorization', b'Basic ' + make_auth(USERNAME, PASSWORD))
+def create_page():
+ data = {
+ 'action': 'create',
+ 'type': 'html'
+ }
try:
- result = urlopen(req, data=gist)
+ response = urlopen('http://www.dinkypage.com', data=postify(data))
except:
- print 'Could not upload results'
return None
- return json.loads(result.read())
-
-
-def get_raw_urls(result):
- files = result.get('files', {})
- for file in files:
- yield files[file].get('raw_url').replace('gist.github.com', 'rawgithub.com')
+ return response.geturl()
+def upload_html(page_url, title, html):
+ data = {
+ 'mode': 'editor',
+ 'title': title,
+ 'html': html,
+ 'ajax': '1'
+ }
+ try:
+ response = urlopen(page_url, data=postify(data))
+ except:
+ return False
+ return 'success' in response.read().decode()
# --- End Web Upload ---
@@ -458,13 +446,12 @@ def main():
debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes')
trysave(html_filename, html)
- result = post_gist(name=html_basename, content=html)
- if result is None:
+ page_url = create_page()
+ if upload_html(page_url, title='OpenSCAD test results', html=html):
+ share_url = page_url.partition('?')[0]
+ print 'html report uploaded at', share_url
+ else:
print 'could not upload html report'
- return
-
- for url in get_raw_urls(result):
- print 'html report uploaded at', url
debug('test_pretty_print complete')
From 58bf7386a11d6f6fb247cf95b89bbbb2682832ca Mon Sep 17 00:00:00 2001
From: a-e-m
Date: Sun, 8 Dec 2013 16:32:36 -0800
Subject: [PATCH 47/66] Added --upload command line option
---
tests/test_pretty_print.py | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index 675867e7..c0d35bb8 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -426,6 +426,11 @@ def main():
if not builddir:
builddir = os.getcwd()
debug('build dir set to ' + builddir)
+
+ upload = False
+ if '--upload' in sys.argv:
+ upload = True
+ debug('will upload test report')
# --- End Command Line Parsing ---
@@ -446,12 +451,13 @@ def main():
debug('saving ' + html_filename + ' ' + str(len(html)) + ' bytes')
trysave(html_filename, html)
- page_url = create_page()
- if upload_html(page_url, title='OpenSCAD test results', html=html):
- share_url = page_url.partition('?')[0]
- print 'html report uploaded at', share_url
- else:
- print 'could not upload html report'
+ if upload:
+ page_url = create_page()
+ if upload_html(page_url, title='OpenSCAD test results', html=html):
+ share_url = page_url.partition('?')[0]
+ print 'html report uploaded at', share_url
+ else:
+ print 'could not upload html report'
debug('test_pretty_print complete')
From a407e4bf29024dbed6f3e89376b8e7d134b98776 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Sun, 8 Dec 2013 23:13:28 -0500
Subject: [PATCH 48/66] Enable upload of test results (#525)
---
tests/CMakeLists.txt | 6 ++++++
tests/CTestCustom.template | 7 ++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index f92eddf5..779ef08d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -904,6 +904,12 @@ string(REPLACE __header__ "Generated by cmake from ${CMAKE_CURRENT_SOURCE_DIR}/C
string(REPLACE __cmake_system_name__ ${CMAKE_SYSTEM_NAME} TMP ${TMP})
string(REPLACE __openscad_binpath__ ${OPENSCAD_BINPATH} TMP ${TMP})
+set(OPENSCAD_UPLOAD_TESTS $ENV{OPENSCAD_UPLOAD_TESTS})
+if (OPENSCAD_UPLOAD_TESTS)
+ set(UPLOADARG "--upload")
+endif()
+string(REPLACE __openscad_upload_tests__ ${UPLOADARG} TMP ${TMP})
+
if (MINGW_CROSS_ENV_DIR)
string(REPLACE __wine__ wine TMP ${TMP})
else()
diff --git a/tests/CTestCustom.template b/tests/CTestCustom.template
index 3f82d734..a01f2b52 100644
--- a/tests/CTestCustom.template
+++ b/tests/CTestCustom.template
@@ -63,7 +63,12 @@ endif()
message("running '__openscad_binpath__ --info' to generate sysinfo.txt")
execute_process(COMMAND __wine__ __openscad_binpath__ --info OUTPUT_FILE sysinfo.txt)
-set(CTEST_CUSTOM_POST_TEST ${CTEST_CUSTOM_POST_TEST} "__cmake_current_binary_dir__/test_pretty_print")
+
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_LESS 2.8.12)
+ set(CTEST_CUSTOM_POST_TEST ${CTEST_CUSTOM_POST_TEST} "__cmake_current_binary_dir__/test_pretty_print")
+else()
+ set(CTEST_CUSTOM_POST_TEST ${CTEST_CUSTOM_POST_TEST} "__python__ __cmake_current_source_dir__/test_pretty_print.py --builddir=__cmake_current_binary_dir__ __openscad_upload_tests__")
+endif()
if ( ${debug_openscad_template} )
foreach(post_test ${CTEST_CUSTOM_POST_TEST} )
From 8f103043a280290802559ce8770c5984f7f0730a Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Sun, 8 Dec 2013 23:16:49 -0500
Subject: [PATCH 49/66] Upload test results from travis
---
scripts/travis-ci.sh | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh
index 9d4258ae..362c2dfa 100755
--- a/scripts/travis-ci.sh
+++ b/scripts/travis-ci.sh
@@ -1,12 +1,7 @@
#!/bin/bash
-qmake && make -j4
-if [[ $? != 0 ]]; then
- echo "Error building OpenSCAD executable"
- exit 1
-fi
cd tests
-cmake .
+cmake -DOPENSCAD_UPLOAD_TESTS=yes .
if [[ $? != 0 ]]; then
echo "Error configuring test suite"
exit 1
From a22394fc392451919f5b378ffe85dbeac7fd3a22 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 9 Dec 2013 00:13:14 -0500
Subject: [PATCH 50/66] Set upload env. variable in travis env since the cmake
version on Travis is too old
---
.travis.yml | 2 ++
scripts/travis-ci.sh | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 9442ca4f..5b9f2156 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,6 +7,8 @@ before_install:
- sudo apt-get install -qq build-essential libqt4-dev libqt4-opengl-dev libxmu-dev cmake bison flex git-core libboost-all-dev libXi-dev libmpfr-dev libboost-dev libglew-dev libeigen2-dev libeigen3-dev libcgal-dev libgmp3-dev libgmp-dev python-paramiko curl imagemagick
- sudo apt-get install -qq libopencsg-dev
+env: OPENSCAD_UPLOAD_TESTS=yes
+
branches:
only:
- travis
diff --git a/scripts/travis-ci.sh b/scripts/travis-ci.sh
index 362c2dfa..9f44b0c9 100755
--- a/scripts/travis-ci.sh
+++ b/scripts/travis-ci.sh
@@ -1,7 +1,7 @@
#!/bin/bash
cd tests
-cmake -DOPENSCAD_UPLOAD_TESTS=yes .
+cmake .
if [[ $? != 0 ]]; then
echo "Error configuring test suite"
exit 1
From 6fd378e9af2dab353764afc4bfad834bd14349b7 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 9 Dec 2013 00:24:55 -0500
Subject: [PATCH 51/66] Set upload env. variable in travis env since the cmake
version on Travis is too old
---
tests/test_pretty_print.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py
index c0d35bb8..0f86cb65 100755
--- a/tests/test_pretty_print.py
+++ b/tests/test_pretty_print.py
@@ -431,6 +431,11 @@ def main():
if '--upload' in sys.argv:
upload = True
debug('will upload test report')
+
+ # Workaround for old cmake's not being able to pass parameters
+ # to CTEST_CUSTOM_POST_TEST
+ if bool(os.getenv("OPENSCAD_UPLOAD_TESTS")):
+ upload = True
# --- End Command Line Parsing ---
From e96305ddf5a728eb323a86e4e900752ae2a6ccfe Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Mon, 9 Dec 2013 00:46:38 -0500
Subject: [PATCH 52/66] minor tuning
---
.travis.yml | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 5b9f2156..c499a8d7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,7 @@
language: cpp
-compiler:
- - gcc
+cache: apt
+compiler: gcc
+
before_install:
- echo 'yes' | sudo add-apt-repository ppa:chrysn/openscad
- sudo apt-get update -qq
@@ -11,6 +12,6 @@ env: OPENSCAD_UPLOAD_TESTS=yes
branches:
only:
- - travis
+ - master
script: ./scripts/travis-ci.sh
From 7cb3ea77ff090e136eb4a9536a394b3766908d17 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Tue, 10 Dec 2013 23:08:04 -0500
Subject: [PATCH 53/66] Build glib2 and gettext on Mac, Find macro for glib2
---
scripts/macosx-build-dependencies.sh | 36 +++++++++++++++++++---------
tests/CMakeLists.txt | 23 +++++++-----------
tests/FindGLIB2.cmake | 27 +++++++++++++++++++++
3 files changed, 60 insertions(+), 26 deletions(-)
create mode 100644 tests/FindGLIB2.cmake
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index 5e26feee..b61c6561 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -285,27 +285,40 @@ build_glew()
make GLEW_DEST=$DEPLOYDIR CC=$CC CFLAGS.EXTRA="-no-cpp-precomp -dynamic -fno-common -mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" LDFLAGS.EXTRA="-mmacosx-version-min=$MAC_OSX_VERSION_MIN $GLEW_EXTRA_FLAGS -arch x86_64" STRIP= install
}
+build_gettext()
+{
+ version=$1
+ echo "Building gettext $version..."
+
+ cd "$BASEDIR"/src
+ rm -rf "gettext-$version"
+ if [ ! -f "glib-$version.tar.xz" ]; then
+ curl --insecure -LO "http://ftpmirror.gnu.org/gettext/gettext-$version.tar.gz"
+ fi
+ tar xzf "gettext-$version.tar.gz"
+ cd "gettext-$version"
+
+ ./configure --prefix="$DEPLOYDIR"
+ make -j4
+ make install
+}
+
build_glib2()
{
- version="$1"
- maj_min_version="${version%.*}" #Drop micro
+ version=$1
+ echo "Building glib2 $version..."
- if [ -e $DEPLOYDIR/lib/glib-2.0 ]; then
- echo "glib2 already installed. not building"
- return
- fi
-
- echo "Building glib2 $version..."
cd "$BASEDIR"/src
rm -rf "glib-$version"
+ maj_min_version="${version%.*}" #Drop micro
if [ ! -f "glib-$version.tar.xz" ]; then
curl --insecure -LO "http://ftp.gnome.org/pub/gnome/sources/glib/$maj_min_version/glib-$version.tar.xz"
fi
tar xJf "glib-$version.tar.xz"
cd "glib-$version"
- ./configure --prefix="$DEPLOYDIR"
- make -j$NUMCPU
+ ./configure --disable-gtk-doc --disable-man --prefix="$DEPLOYDIR" CFLAGS="-I$DEPLOYDIR/include" LDFLAGS="-L$DEPLOYDIR/lib"
+ make -j4
make install
}
@@ -473,7 +486,8 @@ build_boost 1.54.0
# NB! For CGAL, also update the actual download URL in the function
build_cgal 4.3
build_glew 1.10.0
-build_glib2 2.38.1
+build_gettext 0.18.3.1
+build_glib2 2.38.2
build_opencsg 1.3.2
if $OPTION_DEPLOY; then
# build_sparkle andymatuschak 0ed83cf9f2eeb425d4fdd141c01a29d843970c20
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 2533103e..e5900709 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -366,10 +366,10 @@ if (NOT $ENV{CGALDIR} STREQUAL "")
elseif (NOT $ENV{OPENSCAD_LIBRARIES} STREQUAL "")
if (EXISTS "$ENV{OPENSCAD_LIBRARIES}/lib/CGAL")
set(CGAL_DIR "$ENV{OPENSCAD_LIBRARIES}/lib/CGAL")
- set(CMAKE_MODULE_PATH "${CGAL_DIR}")
+ set(CMAKE_MODULE_PATH "${CGAL_DIR}" ${CMAKE_MODULE_PATH})
elseif (EXISTS "$ENV{OPENSCAD_LIBRARIES}/include/CGAL")
set(CGAL_DIR "$ENV{OPENSCAD_LIBRARIES}")
- set(CMAKE_MODULE_PATH "${CGAL_DIR}")
+ set(CMAKE_MODULE_PATH "${CGAL_DIR}" ${CMAKE_MODULE_PATH})
endif()
endif()
message(STATUS "CGAL_DIR: " ${CGAL_DIR})
@@ -413,16 +413,9 @@ if (NOT $ENV{OPENSCAD_LIBRARIES} STREQUAL "")
set(ENV{PKG_CONFIG_LIBDIR} "$ENV{OPENSCAD_LIBRARIES}/lib/pkgconfig")
endif()
-# Find libraries (system installed or dependency built) using pkg-config
-find_package(PkgConfig REQUIRED)
-
-#GLib-2
-pkg_search_module(GLIB2 REQUIRED glib-2.0>=2.2.0)
-#Can't use the CXXFlags directly as they are ;-separated
-string(REPLACE ";" " " GLIB2_CFLAGS "${GLIB2_CFLAGS}")
-message(STATUS "glib-2.0 found: ${GLIB2_VERSION}")
-
-add_definitions(${GLIB2_CFLAGS})
+find_package(GLIB2 2.2.0 REQUIRED)
+add_definitions(${GLIB2_DEFINITIONS})
+inclusion(GLIB2_DIR GLIB2_INCLUDE_DIRS)
# Imagemagick
@@ -595,7 +588,7 @@ set(OFFSCREEN_SOURCES
../src/OpenCSGRenderer.cc)
add_library(tests-core STATIC ${CORE_SOURCES})
-target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} )
+target_link_libraries(tests-core)
set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} )
add_library(tests-common STATIC ${COMMON_SOURCES})
@@ -616,7 +609,7 @@ set(TESTS-NOCGAL-LIBRARIES ${TESTS-CORE-LIBRARIES})
# modulecachetest
#
add_executable(modulecachetest modulecachetest.cc)
-target_link_libraries(modulecachetest tests-nocgal ${TESTS-NOCGAL-LIBRARIES} ${Boost_LIBRARIES})
+target_link_libraries(modulecachetest tests-nocgal ${TESTS-NOCGAL-LIBRARIES})
#
# csgtexttest
@@ -636,7 +629,7 @@ target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${GLEW_LI
#
add_executable(openscad_nogui ../src/openscad.cc)
set_target_properties(openscad_nogui PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -DEIGEN_DONT_ALIGN -DENABLE_CGAL -DENABLE_OPENCSG ${CGAL_CXX_FLAGS_INIT}")
-target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${Boost_LIBRARIES} ${OPENCSG_LIBRARY} ${COCOA_LIBRARY} )
+target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${OPENCSG_LIBRARY} ${COCOA_LIBRARY} )
#
# GUI binary tests
diff --git a/tests/FindGLIB2.cmake b/tests/FindGLIB2.cmake
new file mode 100644
index 00000000..b27b9a9b
--- /dev/null
+++ b/tests/FindGLIB2.cmake
@@ -0,0 +1,27 @@
+find_package(PkgConfig REQUIRED)
+
+pkg_search_module(GLIB2 REQUIRED glib-2.0)
+#message("GLIB2_LIBRARIES ${GLIB2_LIBRARIES}")
+#message("GLIB2_LIBRARY_DIRS ${GLIB2_LIBRARY_DIRS}")
+#message("GLIB2_LDFLAGS ${GLIB2_LDFLAGS}")
+#message("GLIB2_LDFLAGS_OTHER ${GLIB2_LDFLAGS_OTHER}")
+message("GLIB2_INCLUDE_DIRS ${GLIB2_INCLUDE_DIRS}")
+#message("GLIB2_CFLAGS ${GLIB2_CFLAGS}")
+#message("GLIB2_CFLAGS_OTHER ${GLIB2_CFLAGS_OTHER}")
+#message("GLIB2_LIBDIR ${GLIB2_LIBDIR}")
+
+set(GLIB2_DEFINITIONS ${GLIB2_CFLAGS_OTHER})
+#message("GLIB2_DEFINITIONS ${GLIB2_DEFINITIONS}")
+
+set(GLIB2_LIBRARY_NAMES ${GLIB2_LIBRARIES})
+set(GLIB2_LIBRARIES "")
+foreach(GLIB2_LIB ${GLIB2_LIBRARY_NAMES})
+# message("lib: ${GLIB2_LIB}")
+ set(TMP TMP-NOTFOUND)
+ find_library(TMP NAMES ${GLIB2_LIB}
+ PATHS ${GLIB2_LIBRARY_DIRS}
+ NO_DEFAULT_PATH)
+# message("TMP: ${TMP}")
+ list(APPEND GLIB2_LIBRARIES "${TMP}")
+endforeach()
+message("GLIB2_LIBRARIES: ${GLIB2_LIBRARIES}")
From 379e7a05472a50cb417fd0582d2ca4466b2365fe Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Wed, 11 Dec 2013 00:16:27 -0500
Subject: [PATCH 54/66] Updated gmp patch. Fixes #558
---
scripts/macosx-build-dependencies.sh | 81 +++++++++++++++-------------
1 file changed, 45 insertions(+), 36 deletions(-)
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index d4ca1f7c..eadea9f0 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -83,6 +83,51 @@ build_gmp()
fi
tar xjf gmp-$version.tar.bz2
cd gmp-$version
+ patch -p0 gmp-h.in << EOF
+--- gmp-5.1.3/gmp-h.in.old 2013-12-02 20:16:26.000000000 -0800
++++ gmp-5.1.3/gmp-h.in 2013-12-02 20:21:22.000000000 -0800
+@@ -27,13 +27,38 @@
+ #endif
+
+
+-/* Instantiated by configure. */
+ #if ! defined (__GMP_WITHIN_CONFIGURE)
++/* For benefit of fat builds on MacOSX, generate a .h file that can
++ * be used with a universal fat library
++ */
++#if defined(__x86_64__)
++#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
++#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 0
++#define GMP_LIMB_BITS 64
++#define GMP_NAIL_BITS 0
++#elif defined(__i386__)
++#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
++#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 0
++#define GMP_LIMB_BITS 32
++#define GMP_NAIL_BITS 0
++#elif defined(__powerpc64__)
++#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
++#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 1
++#define GMP_LIMB_BITS 64
++#define GMP_NAIL_BITS 0
++#elif defined(__ppc__)
++#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
++#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 1
++#define GMP_LIMB_BITS 32
++#define GMP_NAIL_BITS 0
++#else
++/* For other architectures, fall back on values computed by configure */
+ #define __GMP_HAVE_HOST_CPU_FAMILY_power @HAVE_HOST_CPU_FAMILY_power@
+ #define __GMP_HAVE_HOST_CPU_FAMILY_powerpc @HAVE_HOST_CPU_FAMILY_powerpc@
+ #define GMP_LIMB_BITS @GMP_LIMB_BITS@
+ #define GMP_NAIL_BITS @GMP_NAIL_BITS@
+ #endif
++#endif
+ #define GMP_NUMB_BITS (GMP_LIMB_BITS - GMP_NAIL_BITS)
+ #define GMP_NUMB_MASK ((~ __GMP_CAST (mp_limb_t, 0)) >> GMP_NAIL_BITS)
+ #define GMP_NUMB_MAX GMP_NUMB_MASK
+EOF
+
if $OPTION_32BIT; then
mkdir build-i386
cd build-i386
@@ -119,42 +164,6 @@ build_gmp()
mkdir -p include
cp x86_64/include/gmp.h include/
cp x86_64/include/gmpxx.h include/
-
- patch -p0 include/gmp.h << EOF
---- gmp.h.orig 2011-11-08 01:03:41.000000000 +0100
-+++ gmp.h 2011-11-08 01:06:21.000000000 +0100
-@@ -26,12 +26,28 @@
- #endif
-
-
--/* Instantiated by configure. */
--#if ! defined (__GMP_WITHIN_CONFIGURE)
-+#if defined(__i386__)
-+#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
-+#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 0
-+#define GMP_LIMB_BITS 32
-+#define GMP_NAIL_BITS 0
-+#elif defined(__x86_64__)
- #define __GMP_HAVE_HOST_CPU_FAMILY_power 0
- #define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 0
- #define GMP_LIMB_BITS 64
- #define GMP_NAIL_BITS 0
-+#elif defined(__ppc__)
-+#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
-+#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 1
-+#define GMP_LIMB_BITS 32
-+#define GMP_NAIL_BITS 0
-+#elif defined(__powerpc64__)
-+#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
-+#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 1
-+#define GMP_LIMB_BITS 64
-+#define GMP_NAIL_BITS 0
-+#else
-+#error Unsupported architecture
- #endif
- #define GMP_NUMB_BITS (GMP_LIMB_BITS - GMP_NAIL_BITS)
- #define GMP_NUMB_MASK ((~ __GMP_CAST (mp_limb_t, 0)) >> GMP_NAIL_BITS)
-EOF
}
# As with gmplib, mpfr is built separately in 32-bit and 64-bit mode and then merged
From 745664f1266b3a46898c671ba17db21df19adebf Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Wed, 11 Dec 2013 17:06:27 +1100
Subject: [PATCH 55/66] Ctests build for Glib2 - Add GLIB2_LIBDIR to
find_library paths
---
tests/FindGLIB2.cmake | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tests/FindGLIB2.cmake b/tests/FindGLIB2.cmake
index b27b9a9b..9164c391 100644
--- a/tests/FindGLIB2.cmake
+++ b/tests/FindGLIB2.cmake
@@ -2,13 +2,13 @@ find_package(PkgConfig REQUIRED)
pkg_search_module(GLIB2 REQUIRED glib-2.0)
#message("GLIB2_LIBRARIES ${GLIB2_LIBRARIES}")
-#message("GLIB2_LIBRARY_DIRS ${GLIB2_LIBRARY_DIRS}")
+message("GLIB2_LIBRARY_DIRS ${GLIB2_LIBRARY_DIRS}")
#message("GLIB2_LDFLAGS ${GLIB2_LDFLAGS}")
#message("GLIB2_LDFLAGS_OTHER ${GLIB2_LDFLAGS_OTHER}")
message("GLIB2_INCLUDE_DIRS ${GLIB2_INCLUDE_DIRS}")
#message("GLIB2_CFLAGS ${GLIB2_CFLAGS}")
#message("GLIB2_CFLAGS_OTHER ${GLIB2_CFLAGS_OTHER}")
-#message("GLIB2_LIBDIR ${GLIB2_LIBDIR}")
+message("GLIB2_LIBDIR ${GLIB2_LIBDIR}")
set(GLIB2_DEFINITIONS ${GLIB2_CFLAGS_OTHER})
#message("GLIB2_DEFINITIONS ${GLIB2_DEFINITIONS}")
@@ -20,6 +20,7 @@ foreach(GLIB2_LIB ${GLIB2_LIBRARY_NAMES})
set(TMP TMP-NOTFOUND)
find_library(TMP NAMES ${GLIB2_LIB}
PATHS ${GLIB2_LIBRARY_DIRS}
+ PATHS ${GLIB2_LIBDIR}
NO_DEFAULT_PATH)
# message("TMP: ${TMP}")
list(APPEND GLIB2_LIBRARIES "${TMP}")
From 225afc966be6a99453b2ff2f77c621b7db01b12e Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Wed, 11 Dec 2013 17:53:43 +1100
Subject: [PATCH 56/66] Add gettext lib for linux also
---
scripts/uni-build-dependencies.sh | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh
index ba328b7e..5b9e1290 100755
--- a/scripts/uni-build-dependencies.sh
+++ b/scripts/uni-build-dependencies.sh
@@ -409,6 +409,24 @@ build_glew()
GLEW_DEST=$DEPLOYDIR $MAKER install
}
+build_gettext()
+{
+ version=$1
+ echo "Building gettext $version..."
+
+ cd "$BASEDIR"/src
+ rm -rf "gettext-$version"
+ if [ ! -f "glib-$version.tar.xz" ]; then
+ curl --insecure -LO "http://ftpmirror.gnu.org/gettext/gettext-$version.tar.gz"
+ fi
+ tar xzf "gettext-$version.tar.gz"
+ cd "gettext-$version"
+
+ ./configure --prefix="$DEPLOYDIR"
+ make -j4
+ make install
+}
+
build_glib2()
{
version="$1"
@@ -428,10 +446,9 @@ curl --insecure -LO "http://ftp.gnome.org/pub/gnome/sources/glib/$maj_min_versio
tar xJf "glib-$version.tar.xz"
cd "glib-$version"
- ./configure --prefix="$DEPLOYDIR"
+ ./configure --disable-gtk-doc --disable-man --prefix="$DEPLOYDIR" CFLAGS="-I$DEPLOYDIR/include" LDFLAGS="-L$DEPLOYDIR/lib"
make -j$NUMCPU
make install
-
}
build_opencsg()
@@ -628,6 +645,7 @@ build_boost 1.53.0
build_cgal 4.0.2
build_glew 1.9.0
build_opencsg 1.3.2
+build_gettext 0.18.3.1
build_glib2 2.38.2
echo "OpenSCAD dependencies built and installed to " $BASEDIR
From 4552d2d26352330ea0786dc65bc8f49328f90e02 Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Wed, 11 Dec 2013 17:59:38 +1100
Subject: [PATCH 57/66] Add back in GL and GLIB2 links for tests-core
---
tests/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e5900709..519f1e17 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -588,7 +588,7 @@ set(OFFSCREEN_SOURCES
../src/OpenCSGRenderer.cc)
add_library(tests-core STATIC ${CORE_SOURCES})
-target_link_libraries(tests-core)
+target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} )
set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} )
add_library(tests-common STATIC ${COMMON_SOURCES})
From d14f0be01c06a872a7fb0cef6e0fa67ad7bc4a4f Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Wed, 11 Dec 2013 02:01:50 -0500
Subject: [PATCH 58/66] minor fix: Only use UPLOADARG if it exists
---
tests/CMakeLists.txt | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 779ef08d..370c8be2 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -908,7 +908,9 @@ set(OPENSCAD_UPLOAD_TESTS $ENV{OPENSCAD_UPLOAD_TESTS})
if (OPENSCAD_UPLOAD_TESTS)
set(UPLOADARG "--upload")
endif()
-string(REPLACE __openscad_upload_tests__ ${UPLOADARG} TMP ${TMP})
+if (UPLOADARG)
+ string(REPLACE __openscad_upload_tests__ ${UPLOADARG} TMP ${TMP})
+endif()
if (MINGW_CROSS_ENV_DIR)
string(REPLACE __wine__ wine TMP ${TMP})
From 966f7eb6242cca891b52a0fe4f52775a086c9cd9 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Thu, 12 Dec 2013 01:15:34 -0500
Subject: [PATCH 59/66] #514 Change gcc 4.8.2 version check error to a warning
---
src/version_check.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/version_check.h b/src/version_check.h
index be52e61d..2688f2a5 100644
--- a/src/version_check.h
+++ b/src/version_check.h
@@ -113,7 +113,7 @@ a time, to avoid confusion.
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if GCC_VERSION == 40802
-#error "OpenSCAD isnt compatible with gcc 4.8.2. Please try a different version"
+#warning "gcc 4.8.2 contains a bug causing a crash in CGAL."
#endif
#endif // OPENSCAD_SKIP_VERSION_CHECK
From 68c706da8c9ed5d7bc32d41d7cce86531eb2a673 Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Thu, 12 Dec 2013 17:48:55 +1100
Subject: [PATCH 60/66] Fix check_dependencies for Ubuntu 12.10 and ...
... and other platforms that report a different arch triplet than where
they store their libraries)
check-dependencies.sh doesn't find glib2 on Ubuntu 12.10 since glib is
installed in /usr/lib/i386-linux-gnu/glib-2.0/include/glibconfig.h while
gcc -dumpmachine reports the arch as i686-linux-gnu
---
scripts/check-dependencies.sh | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh
index e5871989..c4312553 100755
--- a/scripts/check-dependencies.sh
+++ b/scripts/check-dependencies.sh
@@ -74,7 +74,19 @@ glib2_sysver()
glib2archtriplet=`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`
fi
glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h
- if [ ! -e $glib2path ]; then return; fi
+ if [ ! -e $glib2path ]; then
+ #No glib found
+ #glib can be installed in /usr/lib/i386-linux-gnu/glib-2.0/ on arch i686-linux-gnu (sometimes?)
+ if [ $glib2archtriplet = "i686-linux-gnu" ]; then
+ glib2archtriplet=i386-linux-gnu
+ glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h
+ if [ ! -e $glib2path ]; then
+ return;
+ fi
+ else
+ return;
+ fi
+ fi
glib2major=`grep "define *GLIB_MAJOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
glib2minor=`grep "define *GLIB_MINOR_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
glib2micro=`grep "define *GLIB_MICRO_VERSION *[0-9.]*" $glib2path | awk '{print $3}'`
From b8859a6716f2e42395ca96ca5c6af10e0d02cec8 Mon Sep 17 00:00:00 2001
From: Brody Kenrick
Date: Thu, 12 Dec 2013 17:54:40 +1100
Subject: [PATCH 61/66] Correct coding style for last commit
(check_dependencies).
---
scripts/check-dependencies.sh | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh
index c4312553..72c4c749 100755
--- a/scripts/check-dependencies.sh
+++ b/scripts/check-dependencies.sh
@@ -80,9 +80,7 @@ glib2_sysver()
if [ $glib2archtriplet = "i686-linux-gnu" ]; then
glib2archtriplet=i386-linux-gnu
glib2path=$1/lib/$glib2archtriplet/glib-2.0/include/glibconfig.h
- if [ ! -e $glib2path ]; then
- return;
- fi
+ if [ ! -e $glib2path ]; then return; fi
else
return;
fi
From 585412bed623dc6adc8df5f27131997e6d2541a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?=
Date: Fri, 13 Dec 2013 15:51:58 +0100
Subject: [PATCH 62/66] Change invalid Programming category to Development
Programming is not a valid category of .desktop file, it is not listed in either [Main Categories](http://standards.freedesktop.org/menu-spec/latest/apa.html) or [Additional Categories](http://standards.freedesktop.org/menu-spec/latest/apas02.html).
desktop-file-validate returns error:
openscad.desktop: error: value "Graphics;3DGraphics;Engineering;Programming;" for key "Categories" in group "Desktop Entry" contains an unregistered value "Programming"; values extending the format should start with "X-"
This changes it to Development, witch is a valid category listed in Additional Categories. Now desktop-file-validate only gives us a hint:
openscad.desktop: hint: value "Graphics;3DGraphics;Engineering;Development;" for key "Categories" in group "Desktop Entry" contains more than one main category; application might appear more than once in the application menu.
That is fine, as we want it both in Graphics and Development.
This is a valid response to #533
---
icons/openscad.desktop | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/icons/openscad.desktop b/icons/openscad.desktop
index f0282acd..46150a21 100644
--- a/icons/openscad.desktop
+++ b/icons/openscad.desktop
@@ -4,4 +4,4 @@ Version=1.0
Name=OpenSCAD
Icon=openscad
Exec=openscad %f
-Categories=Graphics;3DGraphics;Engineering;Programming;
+Categories=Graphics;3DGraphics;Engineering;Development;
From a3aa61bab2c88fce649a9515dec0ddb7d15be085 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Sat, 14 Dec 2013 12:49:23 -0500
Subject: [PATCH 63/66] bugfix: glib include fix
---
glib-2.0.pri | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/glib-2.0.pri b/glib-2.0.pri
index 0fbc4e2d..2f293bf6 100644
--- a/glib-2.0.pri
+++ b/glib-2.0.pri
@@ -7,14 +7,13 @@
# 3. system's standard include paths from pkg-config
glib-2.0 {
-
# read environment variables
OPENSCAD_LIBRARIES_DIR = $$(OPENSCAD_LIBRARIES)
GLIB2_DIR = $$(GLIB2DIR)
!isEmpty(OPENSCAD_LIBRARIES_DIR) {
isEmpty(GLIB2_INCLUDEPATH) {
- GLIB2_INCLUDEPATH_1 = $$OPENSCAD_LIBRARIES_DIR/include/glib-2.0
+ GLIB2_INCLUDEPATH = $$OPENSCAD_LIBRARIES_DIR/include/glib-2.0
GLIB2_INCLUDEPATH_2 = $$OPENSCAD_LIBRARIES_DIR/lib/glib-2.0/include
GLIB2_LIBPATH = $$OPENSCAD_LIBRARIES_DIR/lib
}
@@ -23,7 +22,7 @@ GLIB2_DIR = $$(GLIB2DIR)
isEmpty(GLIB2_INCLUDEPATH) {
GLIB2_CFLAGS = $$system("pkg-config --cflags glib-2.0")
} else {
- GLIB2_CFLAGS = -I$$GLIB2_INCLUDEPATH_1
+ GLIB2_CFLAGS = -I$$GLIB2_INCLUDEPATH
GLIB2_CFLAGS += -I$$GLIB2_INCLUDEPATH_2
}
@@ -35,4 +34,6 @@ isEmpty(GLIB2_LIBPATH) {
QMAKE_CXXFLAGS += $$GLIB2_CFLAGS
LIBS += $$GLIB2_LIBS
+
+message("glib: $$GLIB2_CFLAGS")
}
From cca80a15590c24de9bfd4654b19a64bf1426cf54 Mon Sep 17 00:00:00 2001
From: Marius Kintel
Date: Sat, 14 Dec 2013 17:44:05 -0500
Subject: [PATCH 64/66] bugfix: #562 didn't take into account that it's allowed
with all X coordinates being negative
---
src/PolySetCGALEvaluator.cc | 12 +++++++-----
.../scad/features/rotate_extrude-tests.scad | 3 +++
.../rotate_extrude-tests-expected.png | Bin 17684 -> 18494 bytes
.../rotate_extrude-tests-expected.csg | 7 +++++++
.../rotate_extrude-tests-expected.png | Bin 18320 -> 19174 bytes
.../rotate_extrude-tests-expected.png | Bin 8198 -> 8132 bytes
6 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc
index 599fd7f5..a2d896d8 100644
--- a/src/PolySetCGALEvaluator.cc
+++ b/src/PolySetCGALEvaluator.cc
@@ -455,19 +455,21 @@ PolySet *PolySetCGALEvaluator::rotateDxfData(const RotateExtrudeNode &node, DxfD
for (size_t i = 0; i < dxf.paths.size(); i++)
{
+ double min_x = 0;
double max_x = 0;
for (size_t j = 0; j < dxf.paths[i].indices.size(); j++) {
double point_x = dxf.points[dxf.paths[i].indices[j]][0];
- if (point_x < 0) {
- PRINT("ERROR: all points for rotate_extrude() must have non-negative X coordinates");
- PRINTB("[Point %d on path %d has X coordinate %f]", j % i % point_x);
+ min_x = fmin(min_x, point_x);
+ max_x = fmax(max_x, point_x);
+
+ if ((max_x - min_x) > max_x && (max_x - min_x) > fabs(min_x)) {
+ PRINTB("ERROR: all points for rotate_extrude() must have the same X coordinate sign (range is %.2f -> %.2f)", min_x % max_x);
delete ps;
return NULL;
}
- max_x = fmax(max_x, point_x);
}
- int fragments = get_fragments_from_r(max_x, node.fn, node.fs, node.fa);
+ int fragments = get_fragments_from_r(max_x-min_x, node.fn, node.fs, node.fa);
double ***points;
points = new double**[fragments];
diff --git a/testdata/scad/features/rotate_extrude-tests.scad b/testdata/scad/features/rotate_extrude-tests.scad
index 010b7d22..ec8d1cce 100644
--- a/testdata/scad/features/rotate_extrude-tests.scad
+++ b/testdata/scad/features/rotate_extrude-tests.scad
@@ -32,3 +32,6 @@ translate([50,50,0]) {
// Minimal $fn
translate([0,-60,0]) rotate_extrude($fn=1) translate([20,0,0]) circle(r=10,$fn=1);
+// Object in negative X
+translate([0,60,0]) rotate_extrude() translate([-20,0]) square(10);
+
diff --git a/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png b/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png
index 1488c85ee1a3191248fb329596d427ef2054a276..d198344f2e1fe1088a515b89ac803a010770c4e1 100644
GIT binary patch
literal 18494
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
literal 17684
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 |