Merge branch 'master' of github.com:openscad/openscad into planar

Conflicts:
	src/CGAL_Nef_polyhedron.cc
	tests/CMakeLists.txt
vector-concat
Don Bright 2013-12-15 15:32:06 -06:00
commit 3b3ef44f66
52 changed files with 3175 additions and 585 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
/*.scad
*.dmg
*~
*.tar*
Makefile
objects

17
.travis.yml Normal file
View File

@ -0,0 +1,17 @@
language: cpp
cache: apt
compiler: gcc
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
- sudo apt-get install -qq libopencsg-dev
env: OPENSCAD_UPLOAD_TESTS=yes
branches:
only:
- master
script: ./scripts/travis-ci.sh

View File

@ -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)
@ -93,6 +95,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/)

View File

@ -11,3 +11,4 @@ include(opencsg.pri)
include(glew.pri)
include(eigen.pri)
include(boost.pri)
include(glib-2.0.pri)

39
glib-2.0.pri Normal file
View File

@ -0,0 +1,39 @@
# 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 = $$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
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
message("glib: $$GLIB2_CFLAGS")
}

View File

@ -4,4 +4,4 @@ Version=1.0
Name=OpenSCAD
Icon=openscad
Exec=openscad %f
Categories=Graphics;3DGraphics;Engineering;Programming;
Categories=Graphics;3DGraphics;Engineering;Development;

View File

@ -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
@ -39,6 +39,7 @@ debug: DEFINES += DEBUG
TEMPLATE = app
INCLUDEPATH += src
DEPENDPATH += src
# Handle custom library location.
# Used when manually installing 3rd party libraries
@ -155,6 +156,7 @@ CONFIG += cgal
CONFIG += opencsg
CONFIG += boost
CONFIG += eigen
CONFIG += glib-2.0
#Uncomment the following line to enable QCodeEdit
#CONFIG += qcodeedit
@ -368,6 +370,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 \

View File

@ -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 <sys/types.h> does not define. */
/* #undef off_t */

View File

@ -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<QCocoaModalSessionInfo> cocoaModalSessionStack;
+ static QStack<QCocoaModalSessionInfo> 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<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
+QStack<QCocoaModalSessionInfo> 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();

View File

@ -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<CFArrayRef> 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<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL);
+ QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->postscriptName), 12, NULL);
if (ctFont) {
fontName = CTFontCopyFullName(ctFont);
goto found;

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,31 @@ 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
#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}'`
glib2_sysver_result="${glib2major}.${glib2minor}.${glib2micro}"
}
boost_sysver()
{
boostpath=$1/include/boost/version.hpp
@ -530,7 +555,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"

View File

@ -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
@ -80,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
@ -116,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
@ -220,7 +232,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
@ -282,6 +294,43 @@ 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
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 --disable-gtk-doc --disable-man --prefix="$DEPLOYDIR" CFLAGS="-I$DEPLOYDIR/include" LDFLAGS="-L$DEPLOYDIR/lib"
make -j4
make install
}
build_opencsg()
{
version=$1
@ -446,6 +495,12 @@ 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
<<<<<<< HEAD
build_gettext 0.18.3.1
build_glib2 2.38.2
=======
build_glib2 2.38.1
>>>>>>> d7d5bea7363703c76b9787598304bfc838e893ee
build_opencsg 1.3.2
if $OPTION_DEPLOY; then
# build_sparkle andymatuschak 0ed83cf9f2eeb425d4fdd141c01a29d843970c20

18
scripts/travis-ci.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
cd tests
cmake .
if [[ $? != 0 ]]; then
echo "Error configuring test suite"
exit 1
fi
make -j2
if [[ $? != 0 ]]; then
echo "Error building test suite"
exit 1
fi
ctest -j8
if [[ $? != 0 ]]; then
echo "Test failure"
exit 1
fi

View File

@ -409,6 +409,48 @@ 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"
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 --disable-gtk-doc --disable-man --prefix="$DEPLOYDIR" CFLAGS="-I$DEPLOYDIR/include" LDFLAGS="-L$DEPLOYDIR/lib"
make -j$NUMCPU
make install
}
build_opencsg()
{
if [ -e $DEPLOYDIR/lib/libopencsg.so ]; then
@ -603,5 +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

View File

@ -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
}

View File

@ -64,6 +64,7 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li
<li><a href="http://www.stroustrup.com/C++.html">C++</a>, <a href="http://gcc.gnu.org/">GCC</a>, <a href="http://clang.llvm.org/">clang</a>
<li><a href="http://www.python.org">python</a>
<li><a href="http://nsis.sourceforge.net/Main_Page">Nullsoft installer</a>
<li><a href="https://developer.gnome.org/glib/">GLib</a>
</lu>
</p>

View File

@ -159,9 +159,21 @@ 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<const CGAL_Polyhedron::Vertex::Point_3&(CGAL_Polyhedron::Vertex::*)() const>(&CGAL_Polyhedron::Vertex::point), _1));
bool err = false;
std::string errmsg("");
try {
err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(chN.p3), P );
//chN.p3->convert_to_Polyhedron(P);
} catch (const CGAL::Failure_exception &e) {
err = true;
errmsg = std::string(e.what());
}
if (err) {
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<const CGAL_Polyhedron::Vertex::Point_3&(CGAL_Polyhedron::Vertex::*)() const>(&CGAL_Polyhedron::Vertex::point), _1));
}
}
}
chnode->progress_report();

352
src/CGAL_Nef3_workaround.h Normal file
View File

@ -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 <seel@mpi-sb.mpg.de>
// Miguel Granados <granados@mpi-sb.mpg.de>
// Susan Hert <hert@mpi-sb.mpg.de>
// Lutz Kettner <kettner@mpi-sb.mpg.de>
// Ralf Osbild <osbild@mpi-sb.mpg.de>
// Peter Hachenberger <hachenberger@mpi-sb.mpg.de>
/*
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 <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/assertions.h>
#include <CGAL/Constrained_triangulation_2.h>
#include <CGAL/Triangulation_data_structure_2.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Projection_traits_yz_3.h>
#include <CGAL/Projection_traits_xz_3.h>
#include <CGAL/Constrained_triangulation_face_base_2.h>
#include "printutils.h"
namespace nefworkaround {
template<typename Kernel, typename Nef>
class Triangulation_handler2 {
typedef typename CGAL::Triangulation_vertex_base_2<Kernel> Vb;
typedef typename CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef typename CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef typename CGAL::Constrained_triangulation_2<Kernel,TDS> 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<Face_handle, bool> visited;
CGAL::Unique_hash_map<CTVertex_handle, typename Nef::Vertex_const_handle> 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<typename Triangle_3>
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<typename PIB, typename Index>
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 HDS, typename Kernel, typename Nef>
class Build_polyhedron : public CGAL::Modifier_base<HDS> {
public:
bool error; // added for OpenSCAD
class Visitor {
typedef typename CGAL::Projection_traits_xy_3<Kernel> XY;
typedef typename CGAL::Projection_traits_yz_3<Kernel> YZ;
typedef typename CGAL::Projection_traits_xz_3<Kernel> XZ;
const CGAL::Object_index<typename Nef::Vertex_const_iterator>& VI;
CGAL::Polyhedron_incremental_builder_3<HDS>& B;
const typename Nef::SNC_const_decorator& D;
public:
bool error;//added for OpenSCAD
Visitor(CGAL::Polyhedron_incremental_builder_3<HDS>& BB,
const typename Nef::SNC_const_decorator& sd,
CGAL::Object_index<typename Nef::Vertex_const_iterator>& 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<YZ,Nef> th(f);
th.handle_triangles(B, VI);
} else if(c == 1) {
Triangulation_handler2<XZ,Nef> th(f);
th.handle_triangles(B, VI);
} else if(c == 2) {
Triangulation_handler2<XY,Nef> 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<typename Nef::Vertex_const_iterator> 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<HDS> 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 <typename Kernel>
bool convert_to_Polyhedron( const CGAL::Nef_polyhedron_3<Kernel> &N, CGAL::Polyhedron_3<Kernel> &P )
{
// several lines here added for OpenSCAD
typedef typename CGAL::Nef_polyhedron_3<Kernel> Nef3;
typedef typename CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef typename Polyhedron::HalfedgeDS HalfedgeDS;
CGAL_precondition(N.is_simple());
P.clear();
Build_polyhedron<HalfedgeDS,Kernel,Nef3> bp(N);
P.delegate(bp);
return bp.error;
}
} //namespace nefworkaround
#endif

View File

@ -96,16 +96,23 @@ PolySet *CGAL_Nef_polyhedron::convertToPolyset()
}
else if (this->dim == 3) {
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
ps = new PolySet();
bool err = true;
std::string errmsg("");
CGAL_Polyhedron P;
try {
ps = new PolySet();
CGAL_Polyhedron P;
this->p3->convert_to_Polyhedron(P);
bool err = createPolySetFromPolyhedron(P, *ps);
if (err) delete ps;
err = nefworkaround::convert_to_Polyhedron<CGAL_Kernel3>( *(this->p3), P );
//this->p3->convert_to_Polyhedron(P);
}
catch (const CGAL::Precondition_exception &e) {
delete ps;
PRINTB("CGAL error in CGAL_Nef_polyhedron::convertToPolyset(): %s", e.what());
catch (const CGAL::Failure_exception &e) {
err = true;
errmsg = std::string(e.what());
}
if (!err) err = createPolySetFromPolyhedron(P, *ps);
if (err) {
PRINT("ERROR: CGAL NefPolyhedron->Polyhedron conversion failed.");
if (errmsg!="") PRINTB("ERROR: %s",errmsg);
delete ps; ps = NULL;
}
CGAL::set_error_behaviour(old_behaviour);
}

View File

@ -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());
}

View File

@ -1,6 +1,8 @@
#include "PlatformUtils.h"
#include "boosty.h"
#include <glib.h>
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();

View File

@ -455,12 +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++) {
max_x = fmax(max_x, dxf.points[dxf.paths[i].indices[j]][0]);
double point_x = dxf.points[dxf.paths[i].indices[j]][0];
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;
}
}
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];

View File

@ -27,6 +27,7 @@ using boost::uintmax_t;
#include <CGAL/Cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Nef_polyhedron_3.h>
#include <CGAL_Nef3_workaround.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>

View File

@ -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<CGAL_Kernel3>( *(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);
}

View File

@ -45,6 +45,8 @@
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real.hpp>
/*Unicode support for string lengths and array accesses*/
#include <glib.h>
#ifdef __WIN32__
#include <process.h>
@ -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));
}

View File

@ -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();
}

View File

@ -474,6 +474,13 @@ bool QtUseGUI()
int gui(vector<string> &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));

View File

@ -1,4 +1,4 @@
#if defined(__APPLE__) && defined(__GNUC__)
#if defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__)
#include <iostream>

View File

@ -36,6 +36,8 @@
#include <boost/format.hpp>
#include "boost-utils.h"
#include "boosty.h"
/*Unicode support for string lengths and array accesses*/
#include <glib.h>
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<Value>
{
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;

View File

@ -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
#warning "gcc 4.8.2 contains a bug causing a crash in CGAL."
#endif
#endif // OPENSCAD_SKIP_VERSION_CHECK

View File

@ -11,10 +11,35 @@ module polyhedrons() {
translate([4,0,0])
polyhedron(points = [[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]],
triangles = [[0,2,4],[0,5,2],[0,4,3],[0,3,5],[1,4,2],[1,2,5],[1,3,4], [1,5,3]]);
// Containing concave polygons
translate([6,0,0])
polyhedron(points=[
[-0.8,-0.8,-0.8],
[0,0,-0.8],
[0.8,-0.8,-0.8],
[0.8,0.8,-0.8],
[-0.8,0.8,-0.8],
[-0.8,-0.8,0.8],
[0,0,0.8],
[0.8,-0.8,0.8],
[0.8,0.8,0.8],
[-0.8,0.8,0.8],
],
triangles=[
[0,1,2,3,4],
[5,6,1,0],
[6,7,2,1],
[7,8,3,2],
[8,9,4,3],
[9,5,0,4],
[9,8,7,6,5],
], convexity=2);
}
polyhedrons();
translate([0,2,0]) difference() {
polyhedrons();
translate([2,0,2]) cube([8,3,3], center=true);
translate([3,0,2]) cube([8,3,3], center=true);
}

View File

@ -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);

View File

@ -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 );

44
testdata/scad/misc/string-unicode.scad vendored Normal file
View File

@ -0,0 +1,44 @@
//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 = [
"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 ] );

View File

@ -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)
@ -15,8 +15,15 @@ include(CMakeParseArguments.cmake)
# Detect Lion and force gcc
IF (APPLE)
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++")
# 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")
set(CMAKE_CXX_COMPILER "clang++")
ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.7.0")
@ -359,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})
@ -376,12 +383,40 @@ 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)
#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( 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( 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})
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_package(GLIB2 2.2.0 REQUIRED)
add_definitions(${GLIB2_DEFINITIONS})
inclusion(GLIB2_DIR GLIB2_INCLUDE_DIRS)
# Imagemagick
if (SKIP_IMAGEMAGICK)
@ -553,8 +588,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)
@ -574,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
@ -594,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
@ -774,8 +809,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
@ -795,13 +832,15 @@ list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allexpressions.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allfunctions.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/allmodules.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/nonplanar_polyhedron.scad)
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/nonplanar_polyhedron.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/stl-cgal-convert_to_Polyhedron-crash.scad)
list(APPEND CGALPNGTEST_FILES ${FEATURES_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_FILES})
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/nonplanar_polyhedron.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)
@ -824,6 +863,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
@ -893,6 +936,14 @@ 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()
if (UPLOADARG)
string(REPLACE __openscad_upload_tests__ ${UPLOADARG} TMP ${TMP})
endif()
if (MINGW_CROSS_ENV_DIR)
string(REPLACE __wine__ wine TMP ${TMP})
else()

View File

@ -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} )

28
tests/FindGLIB2.cmake Normal file
View File

@ -0,0 +1,28 @@
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}
PATHS ${GLIB2_LIBDIR}
NO_DEFAULT_PATH)
# message("TMP: ${TMP}")
list(APPEND GLIB2_LIBRARIES "${TMP}")
endforeach()
message("GLIB2_LIBRARIES: ${GLIB2_LIBRARIES}")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -7,6 +7,9 @@ group() {
multmatrix([[1, 0, 0, 4], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
polyhedron(points = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]], faces = [[0, 2, 4], [0, 5, 2], [0, 4, 3], [0, 3, 5], [1, 4, 2], [1, 2, 5], [1, 3, 4], [1, 5, 3]], convexity = 1);
}
multmatrix([[1, 0, 0, 6], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
polyhedron(points = [[-0.8, -0.8, -0.8], [0, 0, -0.8], [0.8, -0.8, -0.8], [0.8, 0.8, -0.8], [-0.8, 0.8, -0.8], [-0.8, -0.8, 0.8], [0, 0, 0.8], [0.8, -0.8, 0.8], [0.8, 0.8, 0.8], [-0.8, 0.8, 0.8]], triangles = [[0, 1, 2, 3, 4], [5, 6, 1, 0], [6, 7, 2, 1], [7, 8, 3, 2], [8, 9, 4, 3], [9, 5, 0, 4], [9, 8, 7, 6, 5]], convexity = 2);
}
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, 2], [0, 0, 1, 0], [0, 0, 0, 1]]) {
difference() {
@ -18,8 +21,11 @@ group() {
multmatrix([[1, 0, 0, 4], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
polyhedron(points = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]], faces = [[0, 2, 4], [0, 5, 2], [0, 4, 3], [0, 3, 5], [1, 4, 2], [1, 2, 5], [1, 3, 4], [1, 5, 3]], convexity = 1);
}
multmatrix([[1, 0, 0, 6], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
polyhedron(points = [[-0.8, -0.8, -0.8], [0, 0, -0.8], [0.8, -0.8, -0.8], [0.8, 0.8, -0.8], [-0.8, 0.8, -0.8], [-0.8, -0.8, 0.8], [0, 0, 0.8], [0.8, -0.8, 0.8], [0.8, 0.8, 0.8], [-0.8, 0.8, 0.8]], triangles = [[0, 1, 2, 3, 4], [5, 6, 1, 0], [6, 7, 2, 1], [7, 8, 3, 2], [8, 9, 4, 3], [9, 5, 0, 4], [9, 8, 7, 6, 5]], convexity = 2);
}
}
multmatrix([[1, 0, 0, 2], [0, 1, 0, 0], [0, 0, 1, 2], [0, 0, 0, 1]]) {
multmatrix([[1, 0, 0, 3], [0, 1, 0, 0], [0, 0, 1, 2], [0, 0, 0, 1]]) {
cube(size = [8, 3, 3], center = true);
}
}

View File

@ -50,4 +50,11 @@ group() {
}
}
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, 60], [0, 0, 1, 0], [0, 0, 0, 1]]) {
rotate_extrude(convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
multmatrix([[1, 0, 0, -20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
square(size = [10, 10], center = false);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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"

View File

@ -0,0 +1,107 @@
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"
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -25,544 +25,447 @@
# 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
try:
from urllib.request import urlopen
from urllib.parse import urlencode
except:
from urllib2 import urlopen
from urllib import urlencode
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:(.*?)<end of 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:(.*?)<end of 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 = """
<h3>[[WIKI_ROOTPATH]] test run report</h3>
'''Sysid''': SYSID
'''Result summary''': NUMPASSED / NUMTESTS tests passed ( PERCENTPASSED % ) <br>
'''System info''':
<pre>
SYSINFO
</pre>
start time: STARTDATE <br>
end time : ENDDATE <br>
'''Image tests'''
<REPEAT1>
{| border=1 cellspacing=0 cellpadding=1
|-
| colspan=2 | FTESTNAME
|-
| Expected image || Actual image
|-
| [[File:EXPECTEDFILE|250px]] || ACTUALFILE_WIKI
|}
<pre>
TESTLOG
</pre>
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 = '''<img src="data:image/png;base64,%s" width="%s" %s/>'''
if data == '':
alt = 'alt="error: no image generated" '
else:
alt = 'alt="openscad_test_image" '
tag %= (data_uri, width, alt)
return tag
</REPEAT1>
'''Text tests'''
<REPEAT2>
{|border=1 cellspacing=0 cellpadding=1
|-
| FTESTNAME
|}
<pre>
TESTLOG
</pre>
</REPEAT2>
'''build.make and flags.make'''
<REPEAT3>
*[[MAKEFILE_NAME]]
</REPEAT3>
"""
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('(<REPEAT1>.*?</REPEAT1>)',s)
repeat2 = ezsearch('(<REPEAT2>.*?</REPEAT2>)',s)
repeat3 = ezsearch('(<REPEAT3>.*?</REPEAT3>)',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('<REPEAT.*?>\n','',s)
s = re.sub('</REPEAT.*?>','',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<pre>\n'+makefiles[mf]+'\n</pre>'
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 = '<img'
tag += ' style="border:1px solid gray"'
tag += ' src="data:image/png;base64,'
tag += data_uri + '"'
tag += ' width="'+str(width)+'"'
if data =='':
tag += ' alt="error: no image generated"'
else:
tag += ' alt="openscad_test_image"'
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 = '<html><head><title>'+wiki_rootpath+' test run for '+sysid +'</title></head><body>'
tail = '</body></html>'
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<h3>'
s+= '\nSystem info\n'
s+= '\n</h3><p>'
s+= '<pre>'+sysinfo+'</pre>\n'
s+= '\n<pre>'
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</pre>'
if not include_passed:
s+= '<h3>Failed tests:</h3>\n'
if len(tests_to_report)==0:
s+= '<p>none</p>'
for t in tests_to_report:
if t.type in ('txt', 'ast', 'csg', 'term', 'echo'):
s+='\n<pre>'+t.fullname+'</pre>\n'
s+='<p><pre>'+t.fulltestlog+'</pre>\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 = <img src='+wikiname_e+' width=250/>'
# imatag_a = <img src='+wikiname_a+' width=250/>'
imgtag_e = png_encode64( t.expectedfile, 250 )
imgtag_a = png_encode64( t.actualfile, 250 )
s+='<table>'
s+='\n<tr><td colspan=2>'+t.fullname
s+='\n<tr><td>Expected<td>Actual'
s+='\n<tr><td>' + imgtag_e + '</td>'
s+='\n <td>' + imgtag_a + '</td>'
s+='\n</table>'
s+='\n<pre>'
s+=t.fulltestlog
s+='\n</pre>'
else:
raise Exception("Unknown test type %r"%t.type)
s+='\n\n<p>\n\n'
s+= '<h3> CMake .build files </h3>\n'
s+= '\n<pre>'
makefiles_wikinames = {}
for mf in sorted(makefiles.keys()):
mfname = mf.strip().lstrip(os.path.sep)
text = open(os.path.join(builddir,mfname)).read()
s+= '</pre><h4>'+mfname+'</h4><pre>'
s+= text
tmp = mf.replace('CMakeFiles','').replace('.dir','')
wikiname = wikify_filename(tmp,wiki_rootpath,sysid)
# s += '\n<a href='+wikiname+'>'+wikiname+'</a><br>'
s+= '\n</pre>'
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 = '''<html>
<head><title>Test run for {sysid}</title>
{style}
</head>
<body>
<h1>{project_name} test run report</h1>
<p>
<b>Sysid</b>: {sysid}
</p>
<p>
<b>Result summary</b>: {numpassed} / {numtests} tests passed ({percent}%)
</p>
<h2>System info</h2>
<pre>{sysinfo}</pre>
<p>start time: {startdate}</p>
<p>end time: {enddate}</p>
<h2>Image tests</h2>
{image_tests}
<h2>Text tests</h2>
{text_tests}
<h2>build.make and flags.make</h2>
{makefiles}
</body></html>'''
style = '''
<style>
body {
color: black;
}
table {
border-collapse: collapse;
}
table td, th {
border: 2px solid gray;
}
.text-name {
border: 2px solid black;
padding: 0.14em;
}
</style>'''
image_template = '''<table>
<tbody>
<tr><td colspan="2">{test_name}</td></tr>
<tr><td> Expected image </td><td> Actual image </td></tr>
<tr><td> {expected} </td><td> {actual} </td></tr>
</tbody>
</table>
<pre>
{test_log}
</pre>
'''
text_template = '''
<span class="text-name">{test_name}</span>
<pre>
{test_log}
</pre>
'''
makefile_template = '''
<h4>{name}</h4>
<pre>
{text}
</pre>
'''
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.get(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
include_passed = 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
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)
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 )
upload = False
if '--upload' in sys.argv:
upload = True
debug('will upload test report')
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'
# 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 ---
imgs, txtpages = towiki(wiki_rootpath, startdate, tests, enddate, sysinfo, sysid, makefiles)
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'
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,'')
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)
print "report saved:\n", 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'
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' )
debug('test_pretty_print complete')
if __name__=='__main__':
main()
main()