Skip to content

Commit

Permalink
Use the same code for loading resources in all executables.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
whitequark committed Nov 28, 2016
1 parent 7681f6d commit e05e78a
Show file tree
Hide file tree
Showing 16 changed files with 172 additions and 223 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ if(NOT WIN32 AND NOT APPLE)
set(GUI gtk2 CACHE STRING "GUI toolkit to use (one of: gtk2 gtk3)")
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)
message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
endif()
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ After that, build SolveSpace as following:
make
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
for use due to bugs in this toolkit.
Expand Down Expand Up @@ -77,7 +77,7 @@ Or, build 64-bit SolveSpace as following:
-DENABLE_TESTS=OFF
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.

Expand All @@ -103,8 +103,8 @@ After that, build SolveSpace as following:
cmake .. -DENABLE_TESTS=OFF
make

The application is built in `build/src/solvespace.app`, and
the executable file is `build/src/solvespace.app/Contents/MacOS/solvespace`.
The application is built in `build/bin/solvespace.app`, and
the executable file is `build/bin/solvespace.app/Contents/MacOS/solvespace`.

[homebrew]: http://brew.sh/

Expand Down
6 changes: 5 additions & 1 deletion bench/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ foreach(pkg_config_lib CAIRO)
endforeach()

add_executable(solvespace_benchmark
harness.cpp)
harness.cpp
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)

target_link_libraries(solvespace_benchmark
solvespace_headless)

add_dependencies(solvespace_benchmark
resources)
58 changes: 1 addition & 57 deletions bench/harness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,6 @@
// Copyright 2016 whitequark
//-----------------------------------------------------------------------------
#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,
std::function<bool()> benchFn,
Expand Down Expand Up @@ -90,14 +41,7 @@ static bool RunBenchmark(std::function<void()> setupFn,
}

int main(int argc, char **argv) {
#if defined(_MSC_VER)
_set_abort_behavior(0, _WRITE_ABORT_MSG);
#endif
#if defined(WIN32)
InitHeaps();
#endif

resourceDir = ResourceRoot();
InitPlatform();

std::string mode, filename;
if(argc == 3) {
Expand Down
19 changes: 12 additions & 7 deletions res/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,23 @@ if(WIN32)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
endfunction()
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)
set(source ${CMAKE_CURRENT_SOURCE_DIR}/${name})
set(target ${app_resource_dir}/${name})
set(resource_list "${resource_list};${target}" PARENT_SCOPE)
set(target_app ${app_resource_dir}/${name})
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(
OUTPUT ${target}
COMMAND ${CMAKE_COMMAND} -E make_directory ${target_dir}
COMMAND ${CMAKE_COMMAND} -E copy ${source} ${target}
OUTPUT ${target_app} ${target_cli}
COMMAND ${CMAKE_COMMAND} -E make_directory ${target_app_dir}
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}"
DEPENDS ${source}
VERBATIM)
Expand Down
12 changes: 9 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ else()
platform/unixutil.cpp)
endif()

if(APPLE)
set(util_LIBRARIES
${APPKIT_LIBRARY})
endif()

# libslvs

set(libslvs_SOURCES
Expand All @@ -45,6 +50,9 @@ target_compile_definitions(slvs
target_include_directories(slvs
PUBLIC ${CMAKE_SOURCE_DIR}/include)

target_link_libraries(slvs
${util_LIBRARIES})

set_target_properties(slvs PROPERTIES
PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/include/slvs.h
VERSION ${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}
Expand Down Expand Up @@ -101,9 +109,6 @@ elseif(APPLE)
set(platform_BUNDLED_LIBS
${PNG_LIBRARIES}
${FREETYPE_LIBRARIES})

set(platform_LIBRARIES
${APPKIT_LIBRARY})
elseif(HAVE_GTK2 OR HAVE_GTK3)
set(platform_SOURCES
platform/gtkmain.cpp
Expand Down Expand Up @@ -192,6 +197,7 @@ add_library(solvespace_cad STATIC

target_link_libraries(solvespace_cad
dxfrw
${util_LIBRARIES}
${ZLIB_LIBRARY}
${PNG_LIBRARY}
${FREETYPE_LIBRARY}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void Slvs_MakeQuaternion(double ux, double uy, double uz,
void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg)
{
if(!IsInit) {
InitHeaps();
InitPlatform();
IsInit = 1;
}

Expand Down
26 changes: 1 addition & 25 deletions src/platform/cocoamain.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
//-----------------------------------------------------------------------------
#include <mach/mach.h>
#include <mach/clock.h>

#import <AppKit/AppKit.h>

#include <iostream>
#include <map>
#import <AppKit/AppKit.h>

#include "solvespace.h"

Expand Down Expand Up @@ -1138,26 +1134,6 @@ bool TextEditControlIsVisible(void) {
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 */

@interface ApplicationDelegate : NSObject<NSApplicationDelegate>
Expand Down
70 changes: 0 additions & 70 deletions src/platform/gtkmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1482,73 +1482,6 @@ std::vector<std::string> GetFontFiles() {
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 */

Expand Down Expand Up @@ -1612,9 +1545,6 @@ int main(int argc, char** argv) {
ambiguous. */
gtk_disable_setlocale();

/* If we're running from the build directory, grab the local resources. */
FindLocalResourceDir(argv[0]);

Gtk::Main main(argc, argv);

#ifdef HAVE_SPACEWARE
Expand Down
25 changes: 0 additions & 25 deletions src/platform/headless.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,31 +251,6 @@ std::vector<std::string> GetFontFiles() {
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
//-----------------------------------------------------------------------------
Expand Down

0 comments on commit e05e78a

Please sign in to comment.