Use the same code for loading resources in all executables.
All of our executables need resources; e.g. the vector font is a resource and it is necessary for generation. Before this commit, the GUI executable loaded the resources in a nice way, and everything else did it in a very ad-hoc, fragile way. After this commit, all executables are placed in <build>/bin and follow the same algorithm: * On Windows, resources are compiled and linked into every executable. * On Linux, resources are copied into <build>/res (which is tried first) and <prefix>/share/solvespace (which is tried second). * On macOS, resources are copied into <build>/res (which is tried first) and <build>/bin/solvespace.app/Contents/Resources (which is tried second). In practice this means that we can add as many executables as we want without duplicating lots of code. In addition, on macOS, we can place supplementary executables into the bundle, and they can use resources from the bundle transparently.pull/36/merge
parent
7681f6df74
commit
9301dec98d
|
@ -47,6 +47,9 @@ if(NOT WIN32 AND NOT APPLE)
|
||||||
set(GUI gtk2 CACHE STRING "GUI toolkit to use (one of: gtk2 gtk3)")
|
set(GUI gtk2 CACHE STRING "GUI toolkit to use (one of: gtk2 gtk3)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
|
if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
|
||||||
message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
|
message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -45,7 +45,7 @@ After that, build SolveSpace as following:
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|
||||||
The application is built as `build/src/solvespace`.
|
The application is built as `build/bin/solvespace`.
|
||||||
|
|
||||||
A fully functional port to GTK3 is available, but not recommended
|
A fully functional port to GTK3 is available, but not recommended
|
||||||
for use due to bugs in this toolkit.
|
for use due to bugs in this toolkit.
|
||||||
|
@ -77,7 +77,7 @@ Or, build 64-bit SolveSpace as following:
|
||||||
-DENABLE_TESTS=OFF
|
-DENABLE_TESTS=OFF
|
||||||
make
|
make
|
||||||
|
|
||||||
The application is built as `build/src/solvespace.exe`.
|
The application is built as `build/bin/solvespace.exe`.
|
||||||
|
|
||||||
Space Navigator support will not be available.
|
Space Navigator support will not be available.
|
||||||
|
|
||||||
|
@ -103,8 +103,8 @@ After that, build SolveSpace as following:
|
||||||
cmake .. -DENABLE_TESTS=OFF
|
cmake .. -DENABLE_TESTS=OFF
|
||||||
make
|
make
|
||||||
|
|
||||||
The application is built in `build/src/solvespace.app`, and
|
The application is built in `build/bin/solvespace.app`, and
|
||||||
the executable file is `build/src/solvespace.app/Contents/MacOS/solvespace`.
|
the executable file is `build/bin/solvespace.app/Contents/MacOS/solvespace`.
|
||||||
|
|
||||||
[homebrew]: http://brew.sh/
|
[homebrew]: http://brew.sh/
|
||||||
|
|
||||||
|
|
10
appveyor.yml
10
appveyor.yml
|
@ -11,15 +11,11 @@ build_script:
|
||||||
- msbuild "src\solvespace.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
- msbuild "src\solvespace.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
- msbuild "test\solvespace_testsuite.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
- msbuild "test\solvespace_testsuite.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
test_script:
|
test_script:
|
||||||
- test\%BUILD_TYPE%\solvespace_testsuite.exe
|
- bin\%BUILD_TYPE%\solvespace_testsuite.exe
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: build\src\Debug\solvespace.exe
|
- path: build\bin\%BUILD_TYPE%\solvespace.exe
|
||||||
name: solvespace.exe
|
name: solvespace.exe
|
||||||
- path: build\src\Debug\solvespace.pdb
|
- path: build\bin\%BUILD_TYPE%\solvespace.pdb
|
||||||
name: solvespace.pdb
|
|
||||||
- path: build\src\RelWithDebInfo\solvespace.exe
|
|
||||||
name: solvespace.exe
|
|
||||||
- path: build\src\RelWithDebInfo\solvespace.pdb
|
|
||||||
name: solvespace.pdb
|
name: solvespace.pdb
|
||||||
deploy:
|
deploy:
|
||||||
# Releases to solvespace/solvespace
|
# Releases to solvespace/solvespace
|
||||||
|
|
|
@ -6,7 +6,11 @@ foreach(pkg_config_lib CAIRO)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_executable(solvespace_benchmark
|
add_executable(solvespace_benchmark
|
||||||
harness.cpp)
|
harness.cpp
|
||||||
|
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
||||||
|
|
||||||
target_link_libraries(solvespace_benchmark
|
target_link_libraries(solvespace_benchmark
|
||||||
solvespace_headless)
|
solvespace_headless)
|
||||||
|
|
||||||
|
add_dependencies(solvespace_benchmark
|
||||||
|
resources)
|
||||||
|
|
|
@ -4,55 +4,6 @@
|
||||||
// Copyright 2016 whitequark
|
// Copyright 2016 whitequark
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
#if defined(WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace SolveSpace {
|
|
||||||
// These are defined in headless.cpp, and aren't exposed in solvespace.h.
|
|
||||||
extern std::string resourceDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string ResourceRoot() {
|
|
||||||
static std::string rootDir;
|
|
||||||
if(!rootDir.empty()) return rootDir;
|
|
||||||
|
|
||||||
// No especially good way to do this, so let's assume somewhere up from
|
|
||||||
// the current directory there's our repository, with CMakeLists.txt, and
|
|
||||||
// pivot from there.
|
|
||||||
#if defined(WIN32)
|
|
||||||
wchar_t currentDirW[MAX_PATH];
|
|
||||||
GetCurrentDirectoryW(MAX_PATH, currentDirW);
|
|
||||||
rootDir = Narrow(currentDirW);
|
|
||||||
#else
|
|
||||||
rootDir = ".";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We're never more than four levels deep.
|
|
||||||
for(size_t i = 0; i < 4; i++) {
|
|
||||||
std::string listsPath = rootDir;
|
|
||||||
listsPath += PATH_SEP;
|
|
||||||
listsPath += "CMakeLists.txt";
|
|
||||||
FILE *f = ssfopen(listsPath, "r");
|
|
||||||
if(f) {
|
|
||||||
fclose(f);
|
|
||||||
rootDir += PATH_SEP;
|
|
||||||
rootDir += "res";
|
|
||||||
return rootDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rootDir[0] == '.') {
|
|
||||||
rootDir += PATH_SEP;
|
|
||||||
rootDir += "..";
|
|
||||||
} else {
|
|
||||||
rootDir.erase(rootDir.rfind(PATH_SEP));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssassert(false, "Couldn't locate repository root");
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool RunBenchmark(std::function<void()> setupFn,
|
static bool RunBenchmark(std::function<void()> setupFn,
|
||||||
std::function<bool()> benchFn,
|
std::function<bool()> benchFn,
|
||||||
|
@ -90,14 +41,7 @@ static bool RunBenchmark(std::function<void()> setupFn,
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
#if defined(_MSC_VER)
|
InitPlatform();
|
||||||
_set_abort_behavior(0, _WRITE_ABORT_MSG);
|
|
||||||
#endif
|
|
||||||
#if defined(WIN32)
|
|
||||||
InitHeaps();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
resourceDir = ResourceRoot();
|
|
||||||
|
|
||||||
std::string mode, filename;
|
std::string mode, filename;
|
||||||
if(argc == 3) {
|
if(argc == 3) {
|
||||||
|
|
|
@ -30,18 +30,23 @@ if(WIN32)
|
||||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
|
||||||
endfunction()
|
endfunction()
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
set(app_resource_dir ${CMAKE_BINARY_DIR}/src/solvespace.app/Contents/Resources)
|
set(app_resource_dir ${CMAKE_BINARY_DIR}/bin/solvespace.app/Contents/Resources)
|
||||||
|
set(cli_resource_dir ${CMAKE_BINARY_DIR}/res)
|
||||||
|
|
||||||
function(add_resource name)
|
function(add_resource name)
|
||||||
set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
|
set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
|
||||||
set(target ${app_resource_dir}/${name})
|
set(target_app ${app_resource_dir}/${name})
|
||||||
set(resource_list "${resource_list};${target}" PARENT_SCOPE)
|
set(target_cli ${cli_resource_dir}/${name})
|
||||||
|
set(resource_list "${resource_list};${target_app};${target_cli}" PARENT_SCOPE)
|
||||||
|
|
||||||
get_filename_component(target_dir ${target} DIRECTORY)
|
get_filename_component(target_app_dir ${target_app} DIRECTORY)
|
||||||
|
get_filename_component(target_cli_dir ${target_cli} DIRECTORY)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${target}
|
OUTPUT ${target_app} ${target_cli}
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${target_dir}
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${target_app_dir}
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target}
|
COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target_app}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${target_cli_dir}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target_cli}
|
||||||
COMMENT "Copying resource ${name}"
|
COMMENT "Copying resource ${name}"
|
||||||
DEPENDS ${source}
|
DEPENDS ${source}
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
|
@ -20,6 +20,11 @@ else()
|
||||||
platform/unixutil.cpp)
|
platform/unixutil.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set(util_LIBRARIES
|
||||||
|
${APPKIT_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
# libslvs
|
# libslvs
|
||||||
|
|
||||||
set(libslvs_SOURCES
|
set(libslvs_SOURCES
|
||||||
|
@ -45,6 +50,9 @@ target_compile_definitions(slvs
|
||||||
target_include_directories(slvs
|
target_include_directories(slvs
|
||||||
PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
target_link_libraries(slvs
|
||||||
|
${util_LIBRARIES})
|
||||||
|
|
||||||
set_target_properties(slvs PROPERTIES
|
set_target_properties(slvs PROPERTIES
|
||||||
PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/include/slvs.h
|
PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/include/slvs.h
|
||||||
VERSION ${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}
|
VERSION ${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}
|
||||||
|
@ -101,9 +109,6 @@ elseif(APPLE)
|
||||||
set(platform_BUNDLED_LIBS
|
set(platform_BUNDLED_LIBS
|
||||||
${PNG_LIBRARIES}
|
${PNG_LIBRARIES}
|
||||||
${FREETYPE_LIBRARIES})
|
${FREETYPE_LIBRARIES})
|
||||||
|
|
||||||
set(platform_LIBRARIES
|
|
||||||
${APPKIT_LIBRARY})
|
|
||||||
elseif(HAVE_GTK2 OR HAVE_GTK3)
|
elseif(HAVE_GTK2 OR HAVE_GTK3)
|
||||||
set(platform_SOURCES
|
set(platform_SOURCES
|
||||||
platform/gtkmain.cpp
|
platform/gtkmain.cpp
|
||||||
|
@ -192,6 +197,7 @@ add_library(solvespace_cad STATIC
|
||||||
|
|
||||||
target_link_libraries(solvespace_cad
|
target_link_libraries(solvespace_cad
|
||||||
dxfrw
|
dxfrw
|
||||||
|
${util_LIBRARIES}
|
||||||
${ZLIB_LIBRARY}
|
${ZLIB_LIBRARY}
|
||||||
${PNG_LIBRARY}
|
${PNG_LIBRARY}
|
||||||
${FREETYPE_LIBRARY}
|
${FREETYPE_LIBRARY}
|
||||||
|
|
|
@ -81,7 +81,7 @@ void Slvs_MakeQuaternion(double ux, double uy, double uz,
|
||||||
void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg)
|
void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg)
|
||||||
{
|
{
|
||||||
if(!IsInit) {
|
if(!IsInit) {
|
||||||
InitHeaps();
|
InitPlatform();
|
||||||
IsInit = 1;
|
IsInit = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,7 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#include <mach/clock.h>
|
#include <mach/clock.h>
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
#import <AppKit/AppKit.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
@ -1138,26 +1134,6 @@ std::vector<std::string> SolveSpace::GetFontFiles() {
|
||||||
return fonts;
|
return fonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *SolveSpace::LoadResource(const std::string &name, size_t *size) {
|
|
||||||
static NSMutableDictionary *cache;
|
|
||||||
|
|
||||||
if(cache == nil) {
|
|
||||||
cache = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *key = [NSString stringWithUTF8String:name.c_str()];
|
|
||||||
NSData *data = [cache objectForKey:key];
|
|
||||||
if(data == nil) {
|
|
||||||
NSString *path = [[NSBundle mainBundle] pathForResource:key ofType:nil];
|
|
||||||
data = [[NSFileHandle fileHandleForReadingAtPath:path] readDataToEndOfFile];
|
|
||||||
ssassert(data != nil, "Cannot find resource");
|
|
||||||
[cache setObject:data forKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
*size = [data length];
|
|
||||||
return [data bytes];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Application lifecycle */
|
/* Application lifecycle */
|
||||||
|
|
||||||
@interface ApplicationDelegate : NSObject<NSApplicationDelegate>
|
@interface ApplicationDelegate : NSObject<NSApplicationDelegate>
|
||||||
|
|
|
@ -1482,73 +1482,6 @@ std::vector<std::string> GetFontFiles() {
|
||||||
return fonts;
|
return fonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ExpandPath(std::string path) {
|
|
||||||
char *expanded_c_path = realpath(path.c_str(), NULL);
|
|
||||||
if(expanded_c_path == NULL) {
|
|
||||||
fprintf(stderr, "realpath(%s): %s\n", path.c_str(), strerror(errno));
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
std::string expanded_path = expanded_c_path;
|
|
||||||
free(expanded_c_path);
|
|
||||||
return expanded_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string resource_dir;
|
|
||||||
void FindLocalResourceDir(const char *argv0) {
|
|
||||||
// Getting path to your own executable is a total portability disaster.
|
|
||||||
// Good job *nix OSes; you're basically all awful here.
|
|
||||||
std::string self_path;
|
|
||||||
#if defined(__linux__)
|
|
||||||
self_path = "/proc/self/exe";
|
|
||||||
#elif defined(__NetBSD__)
|
|
||||||
self_path = "/proc/curproc/exe"
|
|
||||||
#elif defined(__OpenBSD__)
|
|
||||||
self_path = "/proc/curproc/file";
|
|
||||||
#else
|
|
||||||
self_path = argv0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
resource_dir = ExpandPath(self_path);
|
|
||||||
if(resource_dir.empty()) {
|
|
||||||
fprintf(stderr, "Cannot determine path to executable; using global resources.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resource_dir.erase(resource_dir.rfind('/'));
|
|
||||||
resource_dir += "/../res";
|
|
||||||
resource_dir = ExpandPath(resource_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
const void *LoadResource(const std::string &name, size_t *size) {
|
|
||||||
static std::map<std::string, std::vector<uint8_t>> cache;
|
|
||||||
|
|
||||||
auto it = cache.find(name);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
struct stat st;
|
|
||||||
std::string path;
|
|
||||||
|
|
||||||
if(resource_dir.empty()) {
|
|
||||||
path = (UNIX_DATADIR "/") + name;
|
|
||||||
} else {
|
|
||||||
path = resource_dir + "/" + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(stat(path.c_str(), &st)) {
|
|
||||||
ssassert(!stat(path.c_str(), &st), "Cannot find resource");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> data(st.st_size);
|
|
||||||
FILE *f = ssfopen(path.c_str(), "rb");
|
|
||||||
ssassert(f != NULL, "Cannot open resource");
|
|
||||||
fread(&data[0], 1, st.st_size, f);
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
cache.emplace(name, std::move(data));
|
|
||||||
it = cache.find(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
*size = (*it).second.size();
|
|
||||||
return &(*it).second[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Space Navigator support */
|
/* Space Navigator support */
|
||||||
|
|
||||||
|
@ -1612,9 +1545,6 @@ int main(int argc, char** argv) {
|
||||||
ambiguous. */
|
ambiguous. */
|
||||||
gtk_disable_setlocale();
|
gtk_disable_setlocale();
|
||||||
|
|
||||||
/* If we're running from the build directory, grab the local resources. */
|
|
||||||
FindLocalResourceDir(argv[0]);
|
|
||||||
|
|
||||||
Gtk::Main main(argc, argv);
|
Gtk::Main main(argc, argv);
|
||||||
|
|
||||||
#ifdef HAVE_SPACEWARE
|
#ifdef HAVE_SPACEWARE
|
||||||
|
|
|
@ -251,31 +251,6 @@ std::vector<std::string> GetFontFiles() {
|
||||||
return fontFiles;
|
return fontFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string resourceDir;
|
|
||||||
const void *LoadResource(const std::string &name, size_t *size) {
|
|
||||||
static std::map<std::string, std::vector<uint8_t>> cache;
|
|
||||||
|
|
||||||
auto it = cache.find(name);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
std::string path = resourceDir + "/" + name;
|
|
||||||
std::vector<uint8_t> data;
|
|
||||||
|
|
||||||
FILE *f = ssfopen(PathSepUnixToPlatform(path).c_str(), "rb");
|
|
||||||
ssassert(f != NULL, "Cannot open resource");
|
|
||||||
fseek(f, 0, SEEK_END);
|
|
||||||
data.resize(ftell(f));
|
|
||||||
fseek(f, 0, SEEK_SET);
|
|
||||||
fread(&data[0], 1, data.size(), f);
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
cache.emplace(name, std::move(data));
|
|
||||||
it = cache.find(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
*size = (*it).second.size();
|
|
||||||
return &(*it).second[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Application lifecycle
|
// Application lifecycle
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -7,13 +7,18 @@
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
// Copyright 2008-2013 Jonathan Westhues.
|
||||||
// Copyright 2013 Daniel Richard G. <skunk@iSKUNK.ORG>
|
// Copyright 2013 Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include <time.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
# include <strings.h> // for strcasecmp
|
# include <strings.h> // for strcasecmp
|
||||||
|
# include <CoreFoundation/CFString.h>
|
||||||
|
# include <CoreFoundation/CFURL.h>
|
||||||
|
# include <CoreFoundation/CFBundle.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
namespace SolveSpace {
|
namespace SolveSpace {
|
||||||
|
|
||||||
|
@ -89,6 +94,104 @@ void ssremove(const std::string &filename)
|
||||||
remove(filename.c_str());
|
remove(filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string ExpandPath(std::string path) {
|
||||||
|
char *expanded_c_path = realpath(path.c_str(), NULL);
|
||||||
|
if(expanded_c_path == NULL) {
|
||||||
|
fprintf(stderr, "realpath(%s): %s\n", path.c_str(), strerror(errno));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string expanded_path = expanded_c_path;
|
||||||
|
free(expanded_c_path);
|
||||||
|
return expanded_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string &FindLocalResourceDir() {
|
||||||
|
static std::string resourceDir;
|
||||||
|
static bool checked;
|
||||||
|
|
||||||
|
if(checked) return resourceDir;
|
||||||
|
checked = true;
|
||||||
|
|
||||||
|
// Getting path to your own executable is a total portability disaster.
|
||||||
|
// Good job *nix OSes; you're basically all awful here.
|
||||||
|
std::string selfPath;
|
||||||
|
#if defined(__linux__)
|
||||||
|
selfPath = "/proc/self/exe";
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
selfPath = "/proc/curproc/exe"
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
selfPath = "/proc/curproc/file";
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
CFURLRef cfUrl =
|
||||||
|
CFBundleCopyExecutableURL(CFBundleGetMainBundle());
|
||||||
|
CFStringRef cfPath = CFURLCopyFileSystemPath(cfUrl, kCFURLPOSIXPathStyle);
|
||||||
|
selfPath.resize(CFStringGetLength(cfPath) + 1); // reserve space for NUL
|
||||||
|
ssassert(CFStringGetCString(cfPath, &selfPath[0], selfPath.size(), kCFStringEncodingUTF8),
|
||||||
|
"Cannot convert CFString to C string");
|
||||||
|
selfPath.resize(selfPath.size() - 1);
|
||||||
|
CFRelease(cfUrl);
|
||||||
|
CFRelease(cfPath);
|
||||||
|
#else
|
||||||
|
// We don't know how to find the local resource directory on this platform,
|
||||||
|
// so use the global one (by returning an empty string).
|
||||||
|
return resourceDir;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
resourceDir = ExpandPath(selfPath);
|
||||||
|
if(!resourceDir.empty()) {
|
||||||
|
resourceDir.erase(resourceDir.rfind('/'));
|
||||||
|
resourceDir += "/../res";
|
||||||
|
resourceDir = ExpandPath(resourceDir);
|
||||||
|
}
|
||||||
|
if(!resourceDir.empty()) {
|
||||||
|
struct stat st;
|
||||||
|
if(stat(resourceDir.c_str(), &st)) {
|
||||||
|
// We looked at the path where the local resource directory ought to be,
|
||||||
|
// but there isn't one, so use the global one.
|
||||||
|
resourceDir = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resourceDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *LoadResource(const std::string &name, size_t *size) {
|
||||||
|
static std::map<std::string, std::string> cache;
|
||||||
|
|
||||||
|
auto it = cache.find(name);
|
||||||
|
if(it == cache.end()) {
|
||||||
|
const std::string &resourceDir = FindLocalResourceDir();
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
if(resourceDir.empty()) {
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
CFStringRef cfName =
|
||||||
|
CFStringCreateWithCString(kCFAllocatorDefault, name.c_str(),
|
||||||
|
kCFStringEncodingUTF8);
|
||||||
|
CFURLRef cfUrl =
|
||||||
|
CFBundleCopyResourceURL(CFBundleGetMainBundle(), cfName, NULL, NULL);
|
||||||
|
CFStringRef cfPath = CFURLCopyFileSystemPath(cfUrl, kCFURLPOSIXPathStyle);
|
||||||
|
path.resize(CFStringGetLength(cfPath) + 1); // reserve space for NUL
|
||||||
|
ssassert(CFStringGetCString(cfPath, &path[0], path.size(), kCFStringEncodingUTF8),
|
||||||
|
"Cannot convert CFString to C string");
|
||||||
|
path.resize(path.size() - 1);
|
||||||
|
CFRelease(cfName);
|
||||||
|
CFRelease(cfUrl);
|
||||||
|
CFRelease(cfPath);
|
||||||
|
#else
|
||||||
|
path = (UNIX_DATADIR "/") + name;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
path = resourceDir + "/" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssassert(ReadFile(path, &cache[name]), "Cannot read resource");
|
||||||
|
it = cache.find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
*size = (*it).second.size();
|
||||||
|
return static_cast<const void *>(&(*it).second[0]);
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
||||||
// since fragmentation is less of a concern, and it also makes it possible
|
// since fragmentation is less of a concern, and it also makes it possible
|
||||||
|
@ -150,7 +253,7 @@ void MemFree(void *p) {
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitHeaps(void) {
|
void InitPlatform(void) {
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1397,16 +1397,6 @@ static void CreateMainWindows()
|
||||||
ClientIsSmallerBy = (r.bottom - r.top) - (rc.bottom - rc.top);
|
ClientIsSmallerBy = (r.bottom - r.top) - (rc.bottom - rc.top);
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *SolveSpace::LoadResource(const std::string &name, size_t *size) {
|
|
||||||
HRSRC hres = FindResourceW(NULL, Widen(name).c_str(), RT_RCDATA);
|
|
||||||
ssassert(hres != NULL, "Cannot find resource");
|
|
||||||
HGLOBAL res = LoadResource(NULL, hres);
|
|
||||||
ssassert(res != NULL, "Cannot load resource");
|
|
||||||
|
|
||||||
*size = SizeofResource(NULL, hres);
|
|
||||||
return LockResource(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_SPACEWARE
|
#ifdef HAVE_SPACEWARE
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Test if a message comes from the SpaceNavigator device. If yes, dispatch
|
// Test if a message comes from the SpaceNavigator device. If yes, dispatch
|
||||||
|
@ -1474,7 +1464,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||||
ShowWindow(GraphicsWnd, SW_SHOW);
|
ShowWindow(GraphicsWnd, SW_SHOW);
|
||||||
|
|
||||||
// Create the heaps for all dynamic memory (AllocTemporary, MemAlloc)
|
// Create the heaps for all dynamic memory (AllocTemporary, MemAlloc)
|
||||||
InitHeaps();
|
InitPlatform();
|
||||||
|
|
||||||
// Pull out the Unicode command line.
|
// Pull out the Unicode command line.
|
||||||
int argc;
|
int argc;
|
||||||
|
|
|
@ -144,6 +144,16 @@ void ssremove(const std::string &filename)
|
||||||
_wremove(Widen(filename).c_str());
|
_wremove(Widen(filename).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const void *LoadResource(const std::string &name, size_t *size) {
|
||||||
|
HRSRC hres = FindResourceW(NULL, Widen(name).c_str(), RT_RCDATA);
|
||||||
|
ssassert(hres != NULL, "Cannot find resource");
|
||||||
|
HGLOBAL res = ::LoadResource(NULL, hres);
|
||||||
|
ssassert(res != NULL, "Cannot load resource");
|
||||||
|
|
||||||
|
*size = SizeofResource(NULL, hres);
|
||||||
|
return LockResource(res);
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
||||||
// since no fragmentation issues whatsoever, and it also makes it possible
|
// since no fragmentation issues whatsoever, and it also makes it possible
|
||||||
|
@ -182,10 +192,17 @@ void vl() {
|
||||||
ssassert(HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL), "Corrupted heap");
|
ssassert(HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL), "Corrupted heap");
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitHeaps() {
|
void InitPlatform() {
|
||||||
// Create the heap used for long-lived stuff (that gets freed piecewise).
|
// Create the heap used for long-lived stuff (that gets freed piecewise).
|
||||||
PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
|
PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
|
||||||
// Create the heap that we use to store Exprs and other temp stuff.
|
// Create the heap that we use to store Exprs and other temp stuff.
|
||||||
FreeAllTemporary();
|
FreeAllTemporary();
|
||||||
|
|
||||||
|
#if !defined(LIBRARY) && defined(_MSC_VER)
|
||||||
|
// Don't display the abort message; it is aggravating in CLI binaries
|
||||||
|
// and results in infinite WndProc recursion in GUI binaries.
|
||||||
|
_set_abort_behavior(0, _WRITE_ABORT_MSG);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,7 +294,7 @@ void FreeTemporary(void *p);
|
||||||
void FreeAllTemporary();
|
void FreeAllTemporary();
|
||||||
void *MemAlloc(size_t n);
|
void *MemAlloc(size_t n);
|
||||||
void MemFree(void *p);
|
void MemFree(void *p);
|
||||||
void InitHeaps();
|
void InitPlatform();
|
||||||
void vl(); // debug function to validate heaps
|
void vl(); // debug function to validate heaps
|
||||||
|
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
|
@ -58,12 +58,16 @@ set(testsuite_SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(solvespace_testsuite
|
add_executable(solvespace_testsuite
|
||||||
${testsuite_SOURCES})
|
${testsuite_SOURCES}
|
||||||
|
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
||||||
|
|
||||||
target_link_libraries(solvespace_testsuite
|
target_link_libraries(solvespace_testsuite
|
||||||
solvespace_headless
|
solvespace_headless
|
||||||
${COVERAGE_LIBRARY})
|
${COVERAGE_LIBRARY})
|
||||||
|
|
||||||
|
add_dependencies(solvespace_testsuite
|
||||||
|
resources)
|
||||||
|
|
||||||
add_custom_target(solvespace-test ALL
|
add_custom_target(solvespace-test ALL
|
||||||
COMMAND $<TARGET_FILE:solvespace_testsuite>
|
COMMAND $<TARGET_FILE:solvespace_testsuite>
|
||||||
COMMENT "Testing SolveSpace"
|
COMMENT "Testing SolveSpace"
|
||||||
|
|
|
@ -3,18 +3,19 @@
|
||||||
//
|
//
|
||||||
// Copyright 2016 whitequark
|
// Copyright 2016 whitequark
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include "harness.h"
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
|
|
||||||
|
#include "harness.h"
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
#include <windows.h>
|
# include <windows.h>
|
||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace SolveSpace {
|
namespace SolveSpace {
|
||||||
// These are defined in headless.cpp, and aren't exposed in solvespace.h.
|
// These are defined in headless.cpp, and aren't exposed in solvespace.h.
|
||||||
extern std::string resourceDir;
|
|
||||||
extern std::vector<std::string> fontFiles;
|
extern std::vector<std::string> fontFiles;
|
||||||
extern bool antialias;
|
extern bool antialias;
|
||||||
extern std::shared_ptr<Pixmap> framebuffer;
|
extern std::shared_ptr<Pixmap> framebuffer;
|
||||||
|
@ -291,12 +292,7 @@ int Test::Case::Register(Test::Case testCase) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
#if defined(_MSC_VER)
|
InitPlatform();
|
||||||
_set_abort_behavior(0, _WRITE_ABORT_MSG);
|
|
||||||
#endif
|
|
||||||
#if defined(WIN32)
|
|
||||||
InitHeaps();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::regex filter(".*");
|
std::regex filter(".*");
|
||||||
if(argc == 1) {
|
if(argc == 1) {
|
||||||
|
@ -307,10 +303,6 @@ int main(int argc, char **argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceDir = HostRoot();
|
|
||||||
resourceDir.erase(resourceDir.rfind(HOST_PATH_SEP) + 1);
|
|
||||||
resourceDir += "res";
|
|
||||||
|
|
||||||
fontFiles.push_back(HostRoot() + HOST_PATH_SEP + "Gentium-R.ttf");
|
fontFiles.push_back(HostRoot() + HOST_PATH_SEP + "Gentium-R.ttf");
|
||||||
|
|
||||||
// Different Cairo versions have different antialiasing algorithms.
|
// Different Cairo versions have different antialiasing algorithms.
|
||||||
|
|
Loading…
Reference in New Issue