Skip to content

Commit

Permalink
Executable folder utilities (#77)
Browse files Browse the repository at this point in the history
* Add BUILD_UTILITY_LIBRARIES option

* Add whereami dependence

* Add exe relative utilities

* Update samples to use exe relative utilities

* Improve diagnostic on missing file

* Add missing default argument for error param

* Add docs on file utilities

* Add EOL

* Fix typo

Co-authored-by: Ronan Keryell <ronan.keryell@amd.com>

* Simplify byte size calculation

Co-authored-by: Ronan Keryell <ronan.keryell@amd.com>

* Fix typo

Co-authored-by: Ben Ashbaugh <ben.ashbaugh@intel.com>

* Fix formatting

* Remove implicit narrowing conversions

* No unnamed type on libSDK surface

* warning: enumeration value x not handled in switch

---------

Co-authored-by: Ronan Keryell <ronan.keryell@amd.com>
Co-authored-by: Ben Ashbaugh <ben.ashbaugh@intel.com>
  • Loading branch information
3 people committed Nov 16, 2023
1 parent cd612e5 commit de7cb8a
Show file tree
Hide file tree
Showing 22 changed files with 324 additions and 98 deletions.
8 changes: 5 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ project(OpenCL-SDK
)

include(CMakeDependentOption)
option(OPENCL_SDK_BUILD_SAMPLES "Build sample code" ON)
option(OPENCL_SDK_BUILD_UTILITY_LIBRARIES "Build utility libraries" ON)
cmake_dependent_option(OPENCL_SDK_BUILD_SAMPLES "Build sample code" ON OPENCL_SDK_BUILD_UTILITY_LIBRARIES OFF)
cmake_dependent_option(OPENCL_SDK_BUILD_OPENGL_SAMPLES "Build OpenCL-OpenGL interop sample code" ON OPENCL_SDK_BUILD_SAMPLES OFF)
cmake_dependent_option(OPENCL_SDK_TEST_SAMPLES "Add CTest to samples (where applicable)" ON OPENCL_SDK_BUILD_SAMPLES OFF)

Expand All @@ -47,8 +48,9 @@ list(APPEND CMAKE_MODULE_PATH
${PROJECT_SOURCE_DIR}/cmake/Modules
)
include(Dependencies)

add_subdirectory(lib)
if(OPENCL_SDK_BUILD_UTILITY_LIBRARIES)
add_subdirectory(lib)
endif()
if(OPENCL_SDK_BUILD_SAMPLES)
add_subdirectory(samples)
endif()
Expand Down
7 changes: 7 additions & 0 deletions cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
if(OPENCL_SDK_BUILD_UTILITY_LIBRARIES)
foreach(DEP IN ITEMS whereami)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Dependencies/${DEP}")
include(${DEP})
endforeach()
endif()

if(OPENCL_SDK_BUILD_SAMPLES)
foreach(DEP IN ITEMS cargs TCLAP Stb)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Dependencies/${DEP}")
Expand Down
15 changes: 15 additions & 0 deletions cmake/Dependencies/whereami/whereami.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.11)
include(FetchContent)
FetchContent_Declare(
whereami-external
GIT_REPOSITORY https://github.com/gpakosz/whereami.git
GIT_TAG ba364cd54fd431c76c045393b6522b4bff547f50 # master @ 2023.04.20.
)
FetchContent_MakeAvailable(whereami-external)
add_library(whereami IMPORTED INTERFACE)
target_include_directories(whereami
INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/_deps/whereami-external-src/src"
)
target_sources(whereami
INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/_deps/whereami-external-src/src/whereami.c"
)
2 changes: 2 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ foreach(UTIL_LIB_NAME IN ITEMS Utils UtilsCpp)
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)
target_link_libraries(${UTIL_LIB_TARGET}
PRIVATE
whereami
PUBLIC
${UTIL_LIB_DEPS}
OpenCL::OpenCL
Expand Down
95 changes: 95 additions & 0 deletions lib/Utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The OpenCL Utility Library provides both C and C++ bindings with near feature pa
- [Context](#context-utilities)
- [Event](#event-utilities)
- [Error](#error-handling-utilities)
- [File](#file-utilities)

### Platform utilities

Expand Down Expand Up @@ -119,3 +120,97 @@ public:
};
```
This type is used as the exception type thrown by utilities when an error occurs and the compiling code defines `CL_HPP_ENABLE_EXCEPTIONS`

### File utilities

```c
char* cl_util_read_text_file(
const char* const filename,
size_t* const length,
cl_int* const error);
```
```c++
std::string cl::util::read_text_file(
const char* const filename,
cl_int* const error = nullptr);
```

These functions read a text file into memory, where `filename` is evaluated relative to the current working directory. The C-version contains a terminating null and takes an optional pointer to `length` by which the length my be returned, potentially saving a subsequent call to `strlen`. The function hands ownership of the allocated storage to the caller.

```c
unsigned char* cl_util_read_binary_file(
const char* const filename,
size_t* const length,
cl_int* const error);
```
```c++
std::vector<unsigned char> read_binary_file(
const char* const filename,
cl_int* const error = nullptr);
```

These functions read a binary file into memory, where `filename` is evaluated relative to the current working directory. The C-version takes an optional pointer to `length` by which the length my be returned. Because it's binary data, it is _not_ null-terminated, therefore highly recommended to take it's size. The returned types align with OpenCL APIs taking binaries as input. The function hands ownership of the allocated storage to the caller.

```c
cl_program cl_util_read_binaries(
const cl_context context,
const cl_device_id* const devices,
const cl_uint num_devices,
const char* const program_file_name,
cl_int* const error
);
```

```c++
Program::Binaries read_binary_files(
const std::vector<cl::Device>& devices,
const char* const program_file_name,
cl_int* const error = nullptr);
```
These functions read a set of binary files into memory. `program_file_name` is a pattern that will be completed for every input device using the `"(program_file_name)_(name of device).bin"` pattern. If any of the files are not found, the function fails.
```c
cl_int cl_util_write_binaries(
const cl_program program,
const char* const program_file_name);
```

```c++
write_binaries(
const cl::Program::Binaries& binaries,
const std::vector<cl::Device>& devices,
const char* const program_file_name);
```
These functions will write all device binaries of a program to persistent storage. `program_file_name` is a pattern that will be completed for every input device using the `"(program_file_name)_(name of device).bin"` pattern.
```c
cl_int cl_util_executable_folder(
char* filename,
size_t* const length);
```

```c++
std::string executable_folder(
cl_int* const error = nullptr);
```
These functions return the path to the folder containing the currently running executable. It is typically useful to find assets which are stored uniformly in a program's build and install tree. The C-version contains a terminating null and takes an optional pointer to `length` by which the length my be returned, potentially saving a subsequent call to `strlen`.
```c
char* cl_util_read_exe_relative_text_file(
const char* const rel_path,
size_t* const length,
cl_int* const error);
```

```c++
std::string read_exe_relative_text_file(
const char* const filename,
cl_int* const error = nullptr);
```
These functions read a text file into memory, where `filename` is evaluated relative to the executable currently running. The C-version contains a terminating null and takes an optional pointer to `length` by which the length will be returned, potentially saving a subsequent call to `strlen`. The function hands ownership of the allocated storage to the caller.
7 changes: 6 additions & 1 deletion lib/include/CL/SDK/InteropWindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@
#include <SFML/Window.hpp>
#include <SFML/OpenGL.hpp>

// STL includes
#include <type_traits>

namespace cl {
namespace sdk {
class SDKCPP_EXPORT InteropWindow : public sf::Window {
public:
using Style = std::underlying_type_t<decltype(sf::Style::Default)>;

explicit InteropWindow(
sf::VideoMode mode, const sf::String& title,
sf::Uint32 style = sf::Style::Default,
Style style = sf::Style::Default,
const sf::ContextSettings& settings = sf::ContextSettings{},
cl_uint platform_id = 0, cl_uint device_id = 0,
cl_bitfield device_type = CL_DEVICE_TYPE_DEFAULT);
Expand Down
13 changes: 13 additions & 0 deletions lib/include/CL/Utils/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,16 @@ cl_program cl_util_read_binaries(const cl_context context,
const cl_uint num_devices,
const char* const program_file_name,
cl_int* const error);

// returns the folder containing the running executable
UTILS_EXPORT
cl_int cl_util_executable_folder(char* filename, size_t* const length);

// read all the text file contents securely in ANSI C89
// return pointer to C-string with file contents
// interprets filename relative to the folder containing
// the running executable
UTILS_EXPORT
char* cl_util_read_exe_relative_text_file(const char* const rel_path,
size_t* const length,
cl_int* const error);
16 changes: 11 additions & 5 deletions lib/include/CL/Utils/File.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,24 @@ namespace cl {
namespace util {

std::string UTILSCPP_EXPORT read_text_file(const char* const filename,
cl_int* const error);
cl_int* const error = nullptr);

std::vector<unsigned char> UTILSCPP_EXPORT
read_binary_file(const char* const filename, cl_int* const error);
read_binary_file(const char* const filename, cl_int* const error = nullptr);

Program::Binaries UTILSCPP_EXPORT
read_binary_files(const std::vector<cl::Device>& devices,
const char* const program_file_name, cl_int* const error);
Program::Binaries UTILSCPP_EXPORT read_binary_files(
const std::vector<cl::Device>& devices,
const char* const program_file_name, cl_int* const error = nullptr);

cl_int UTILSCPP_EXPORT
write_binaries(const cl::Program::Binaries& binaries,
const std::vector<cl::Device>& devices,
const char* const program_file_name);

std::string UTILSCPP_EXPORT
executable_folder(cl_int* const error = nullptr);

std::string UTILSCPP_EXPORT read_exe_relative_text_file(
const char* const filename, cl_int* const error = nullptr);
}
}
2 changes: 1 addition & 1 deletion lib/src/SDK/Image.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ cl_sdk_image cl_sdk_read_image(const char* const file_name, cl_int* const error)

static char* to_lowercase(const char* const s, char* const d, const size_t n)
{
for (size_t i = 0; i < n; ++i) d[i] = tolower(s[i]);
for (size_t i = 0; i < n; ++i) d[i] = (char)tolower(s[i]);
return d;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/src/SDK/InteropWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#include <CL/SDK/InteropContext.hpp>

cl::sdk::InteropWindow::InteropWindow(sf::VideoMode mode,
const sf::String& title, sf::Uint32 style,
const sf::String& title, Style style,
const sf::ContextSettings& settings,
cl_uint platform_id, cl_uint device_id,
cl_bitfield device_type)
: sf::Window{ mode, title, style, settings }, plat_id{ platform_id },
dev_id{ device_id }, dev_type{ device_type }
: sf::Window{ mode, title, static_cast<sf::Uint32>(style), settings },
plat_id{ platform_id }, dev_id{ device_id }, dev_type{ device_type }
{}

void cl::sdk::InteropWindow::run()
Expand Down
76 changes: 76 additions & 0 deletions lib/src/Utils/File.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include <stdio.h> // fopen, ferror, fread, fclose
#include <string.h> // memset

// whereami includes
#include <whereami.h>

// read all the text file contents securely in ANSI C89
// return pointer to C-string with file contents
// can handle streams with no known size and no support for fseek
Expand Down Expand Up @@ -337,3 +340,76 @@ cl_program cl_util_read_binaries(const cl_context context,
if (error != NULL) *error = err;
return program;
}

UTILS_EXPORT
cl_int cl_util_executable_folder(char *const filename, size_t *const length)
{
cl_int err = CL_SUCCESS;
int wai_length, wai_dirname_length = 0;
char *wai_filename = NULL;

#define IF_ERR(func, error_type, label) \
do \
{ \
if (func) \
{ \
err = error_type; \
goto label; \
} \
} while (0)

wai_length = wai_getExecutablePath(NULL, 0, NULL);
IF_ERR(wai_length == -1, CL_UTIL_FILE_OPERATION_ERROR, end);
MEM_CHECK(wai_filename = (char *)malloc(wai_length), err, end);
IF_ERR(wai_getExecutablePath(wai_filename, wai_length, &wai_dirname_length)
== -1,
CL_UTIL_FILE_OPERATION_ERROR, end);

if (length != NULL)
{
*length = (int)wai_dirname_length + 1;
}

if (filename != NULL)
{
memmove(filename, wai_filename, wai_dirname_length);
filename[wai_dirname_length] = '\0';
}

end:
free(wai_filename);

return err;
}

// read all the text file contents securely in ANSI C89
// return pointer to C-string with file contents
// interprets filename relative to the folder containing
// the running executable
UTILS_EXPORT
char *cl_util_read_exe_relative_text_file(const char *const rel_path,
size_t *const length,
cl_int *const error)
{
char *result = NULL;
size_t result_size = 0;
char *abs_path = NULL;
cl_int err = CL_SUCCESS;
size_t exe_folder_length;
OCLERROR_RET(cl_util_executable_folder(NULL, &exe_folder_length), err, end);
MEM_CHECK(abs_path =
(char *)malloc(exe_folder_length + strlen(rel_path) + 1),
err, end);
OCLERROR_RET(cl_util_executable_folder(abs_path, NULL), err, end);
strcat(strcat(abs_path, "/"), rel_path);
OCLERROR_PAR(result = cl_util_read_text_file(abs_path, &result_size, &err),
err, end);

if (length != NULL) *length = result_size;

if (error != NULL) *error = err;
end:
free(abs_path);

return result;
}
Loading

0 comments on commit de7cb8a

Please sign in to comment.