Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compilation with Emscripten #52

Merged
merged 12 commits into from
Dec 24, 2021
8 changes: 6 additions & 2 deletions .github/setenv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ function c4_cfg_test()
-DCMAKE_C_FLAGS="-std=c99 -m$bits" -DCMAKE_CXX_FLAGS="-m$bits"
cmake --build $build_dir --target help | sed 1d | sort
;;
em++)
emcmake cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \
-DCMAKE_BUILD_TYPE=$BT $CMFLAGS -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0"
;;
*)
echo "unknown compiler"
exit 1
Expand Down Expand Up @@ -367,7 +371,7 @@ function _c4_parallel_build_flags()
echo "-IDEBuildOperationMaxNumberOfConcurrentCompileTasks=$NUM_JOBS_BUILD"
fi
;;
*g++*|*gcc*|*clang*)
*g++*|*gcc*|*clang*|em++)
if [ -z "$NUM_JOBS_BUILD" ] ; then
echo "-j $(nproc)"
else
Expand All @@ -394,7 +398,7 @@ function _c4_generator_build_flags()
# https://stackoverflow.com/questions/51153525/xcode-10-unable-to-attach-db-error
echo "-UseModernBuildSystem=NO"
;;
*g++*|*gcc*|*clang*)
*g++*|*gcc*|*clang*|em++)
;;
"") # allow empty compiler
;;
Expand Down
95 changes: 95 additions & 0 deletions .github/workflows/emscripten.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: emscripten

defaults:
#if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP
run:
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash -e -x {0}

on:
# https://git.luolix.topmunity/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master

env:
PROJ_PFX_TARGET: c4core-
PROJ_PFX_CMAKE: C4CORE_
CMAKE_FLAGS:
NUM_JOBS_BUILD: # 4
EMSCRIPTEN_CACHE_FOLDER: 'emsdk-cache'


# ubuntu-20.04:
# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md
# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0
# clang: 8.0.1, 9.0.1, 10.0.0
# ubuntu-18.04:
# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md
# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0
# clang: 6.0.0, 8.0.0, 9.0.0
# macos-11.0: macOS Big Sur 11.0
# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md
# Xcode 12.1 11.7
# clang/LLVM 10.0.1
# gcc-8 gcc-9
# macos-10.15: macOS Catalina 10.15
# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md
# Xcode 12.1 11.7
# clang/LLVM 11.0.0
# gcc-8 gcc-9
# windows-2019:
# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md
# vs2019
# windows-2016:
# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md
# vs2017

jobs:

#----------------------------------------------------------------------------
emscripten:
name: emscripten/${{matrix.emver}}/c++${{matrix.std}}/${{matrix.bt}}
continue-on-error: true
if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct
runs-on: ${{matrix.os}}
strategy:
fail-fast: false
matrix:
include:
- {std: 11, cxx: em++, emver: 2.0.34, bt: Debug , os: ubuntu-latest, bitlinks: static32}
- {std: 11, cxx: em++, emver: 2.0.34, bt: Release, os: ubuntu-latest, bitlinks: static32}
- {std: 20, cxx: em++, emver: 2.0.34, bt: Debug , os: ubuntu-latest, bitlinks: static32}
- {std: 20, cxx: em++, emver: 2.0.34, bt: Release, os: ubuntu-latest, bitlinks: static32}
- {std: 11, cxx: em++, emver: 3.0.0 , bt: Debug , os: ubuntu-latest, bitlinks: static32}
- {std: 11, cxx: em++, emver: 3.0.0 , bt: Release, os: ubuntu-latest, bitlinks: static32}
- {std: 20, cxx: em++, emver: 3.0.0 , bt: Debug , os: ubuntu-latest, bitlinks: static32}
- {std: 20, cxx: em++, emver: 3.0.0 , bt: Release, os: ubuntu-latest, bitlinks: static32}
env:
STD: "${{matrix.std}}"
CXX_: "${{matrix.cxx}}"
BT: "${{matrix.bt}}"
BITLINKS: "${{matrix.bitlinks}}"
VG: "${{matrix.vg}}"
SAN: "${{matrix.san}}"
LINT: "${{matrix.lint}}"
OS: "${{matrix.os}}"
steps:
- {name: checkout, uses: actions/checkout@v2, with: {submodules: recursive}}
- name: setup emscripten cache
id: cache-system-libraries
uses: actions/cache@v2
with: {path: "${{env.EMSCRIPTEN_CACHE_FOLDER}}", key: "${{matrix.emver}}-${{runner.os}}"}
- name: setup emscripten
uses: mymindstorm/setup-emsdk@v11
with: {version: "${{matrix.emver}}", actions-cache-folder: "${{env.EMSCRIPTEN_CACHE_FOLDER}}"}
- {name: show info, run: source .github/setenv.sh && c4_show_info}
- name: static32-configure---------------------------------------------------
run: source .github/setenv.sh && c4_cfg_test static32
- {name: static32-build, run: source .github/setenv.sh && c4_build_test static32}
- {name: static32-run, run: source .github/setenv.sh && c4_run_test static32}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ I've been using these utilities in this or similar forms for some years now,
and I've found them incredibly useful in my projects. I'm packing these as a
separate library, as all of my projects use it.

c4core is [extensively unit-tested in Linux, Windows and
MacOS](https://github.com/biojppm/c4core/actions). The tests cover
x64, x86, arm, wasm (emscripten), aarch64, ppc64le and s390x
architectures, and include analysing c4core with:
* valgrind
* clang-tidy
* clang sanitizers:
* memory
* address
* undefined behavior
* thread
* [LGTM.com](https://lgtm.com/projects/g/biojppm/c4core)


## Obtaining c4core

Expand Down
4 changes: 4 additions & 0 deletions changelog/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
```
- Add `csubstr::is_unsigned_integer()` and `csubstr::is_real()` ([PR #49](https://github.com/biojppm/c4core/pull/49)).
- CMake: add alias target c4core::c4core, guaranteeing that the same code can be used with `add_subdirectory()` and `find_package()`. (see [rapidyaml #173](https://github.com/biojppm/rapidyaml/issues/173))
- Add support for compilation with emscripten (WebAssembly+javascript) ` ([PR #52](https://github.com/biojppm/c4core/pull/52)).


### Fixes
Expand All @@ -37,3 +38,6 @@
- In `c4/charconv.hpp`: do not use C4_ASSERT in `to_c_fmt()`, which is `constexpr`.


### Thanks

- @cschreib
25 changes: 18 additions & 7 deletions src/c4/bitmask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ bm2stream(Stream &s, Enum value, EnumOffsetType offst=EOFFS_PFX)

// some utility macros, undefed below

/// @cond dev

/** Execute @p code if the @p num of characters is available in the str
/* Execute `code` if the `num` of characters is available in the str
* buffer. This macro simplifies the code for bm2str().
* @todo improve this by writing from the end and moving only once. */
* @todo improve performance by writing from the end and moving only once. */
#define _c4prependchars(code, num) \
if(str && (pos + num <= sz)) \
{ \
Expand All @@ -100,19 +101,29 @@ bm2stream(Stream &s, Enum value, EnumOffsetType offst=EOFFS_PFX)
/* now write in the beginning of the string */ \
code; \
} \
else if(str && sz) { C4_ERROR("cannot write to string pos=%d num=%d sz=%d", (int)pos, (int)num, (int)sz); } \
else if(str && sz) \
{ \
C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \
(int)pos, (int)num, (int)sz); \
} \
pos += num

/** Execute @p code if the @p num of characters is available in the str
/* Execute `code` if the `num` of characters is available in the str
* buffer. This macro simplifies the code for bm2str(). */
#define _c4appendchars(code, num) \
if(str && (pos + num <= sz)) \
{ \
code; \
} \
else if(str && sz) { C4_ERROR("cannot write to string pos=%d num=%d sz=%d", (int)pos, (int)num, (int)sz); } \
else if(str && sz) \
{ \
C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \
(int)pos, (int)num, (int)sz); \
} \
pos += num

/// @endcond


/** convert a bitmask to string.
* return the number of characters written. To find the needed size,
Expand All @@ -131,13 +142,13 @@ size_t bm2str

auto syms = esyms<Enum>();
size_t pos = 0;
typename EnumSymbols<Enum>::Sym const* zero = nullptr;
typename EnumSymbols<Enum>::Sym const* C4_RESTRICT zero = nullptr;

// do reverse iteration to give preference to composite enum symbols,
// which are likely to appear later in the enum sequence
for(size_t i = syms.size()-1; i != size_t(-1); --i)
{
auto const p = syms[i];
auto const &C4_RESTRICT p = syms[i]; // do not copy, we are assigning to `zero`
I b = static_cast<I>(p.value);
if(b == 0)
{
Expand Down
12 changes: 8 additions & 4 deletions src/c4/charconv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1209,8 +1209,10 @@ inline bool atod(csubstr str, double * C4_RESTRICT v)
inline size_t atof_first(csubstr str, float * C4_RESTRICT v)
{
csubstr trimmed = str.first_real_span();
if(trimmed.len == 0) return csubstr::npos;
if(atof(trimmed, v)) return static_cast<size_t>(trimmed.end() - str.begin());
if(trimmed.len == 0)
return csubstr::npos;
if(atof(trimmed, v))
return static_cast<size_t>(trimmed.end() - str.begin());
return csubstr::npos;
}

Expand All @@ -1222,8 +1224,10 @@ inline size_t atof_first(csubstr str, float * C4_RESTRICT v)
inline size_t atod_first(csubstr str, double * C4_RESTRICT v)
{
csubstr trimmed = str.first_real_span();
if(trimmed.len == 0) return csubstr::npos;
if(atod(trimmed, v)) return static_cast<size_t>(trimmed.end() - str.begin());
if(trimmed.len == 0)
return csubstr::npos;
if(atod(trimmed, v))
return static_cast<size_t>(trimmed.end() - str.begin());
return csubstr::npos;
}

Expand Down
4 changes: 4 additions & 0 deletions src/c4/cpu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EB

#elif defined(__EMSCRIPTEN__)
# define C4_BYTE_ORDER _C4EL
# define C4_WORDSIZE 4

#elif defined(SWIG)
#error "please define CPU architecture macros when compiling with swig"

Expand Down
4 changes: 4 additions & 0 deletions src/c4/std/vector_fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ template<typename T, typename Alloc> class vector;
#elif defined(_LIBCPP_VERSION)
namespace std {
template<typename> class allocator;
#if defined(__EMSCRIPTEN__)
inline namespace __2 {
#else
inline namespace __1 {
#endif
template<typename T, typename Alloc> class vector;
} // namespace __1
} // namespace std
Expand Down
28 changes: 20 additions & 8 deletions test/test_charconv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1162,7 +1162,7 @@ void test_rtoa(substr buf, Real f, rtoa_fn_t<Real> rtoa_fn, int precision, const
size_t ret;

INFO("num=" << f << " precision=" << precision
<< "' hexa='" << hexa << "' hexa_alternative='" << hexa_alternative << "'");
<< " hexa='" << hexa << "' hexa_alternative='" << hexa_alternative << "'");

memset(buf.str, 0, buf.len);
ret = rtoa_fn(buf, f, precision, FTOA_SCIENT);
Expand All @@ -1186,7 +1186,7 @@ void test_rtoa(substr buf, Real f, rtoa_fn_t<Real> rtoa_fn, int precision, const
std::string report;
from_chars(buf.left_of(ret), &report);
bool ok = buf.left_of(ret) == to_csubstr(hexa) || buf.left_of(ret) == to_csubstr(hexa_alternative);
CHECK_MESSAGE(ok, "ret='" << report);
CHECK_MESSAGE(ok, "ret='" << report << "'");
}


Expand All @@ -1196,6 +1196,18 @@ TEST_CASE("ftoa.basic")
substr buf(bufc);
C4_ASSERT(buf.len == sizeof(bufc)-1);

// earlier versions of emscripten's sprintf() do not respect some
// precision values when printing in hexadecimal format.
//
// @see https://github.com/biojppm/c4core/pull/52
#if defined(__EMSCRIPTEN__) && __EMSCRIPTEN_major__ < 3
#define _c4emscripten_alt(alt) , alt
#define _c4emscripten_alt2(alt1, alt2) , alt2
#else
#define _c4emscripten_alt(alt)
#define _c4emscripten_alt2(alt1, alt2) , alt1
#endif

float f = 1.1234123f;
double d = 1.1234123;

Expand All @@ -1205,11 +1217,11 @@ TEST_CASE("ftoa.basic")
test_rtoa(buf, f, &ftoa, 1, /*scient*/"1.1e+00", /*flt*/"1.1", /*flex*/"1.1", /*hexa*/"0x1.2p+0");
test_rtoa(buf, d, &dtoa, 1, /*scient*/"1.1e+00", /*flt*/"1.1", /*flex*/"1.1", /*hexa*/"0x1.2p+0");

test_rtoa(buf, f, &ftoa, 2, /*scient*/"1.12e+00", /*flt*/"1.12", /*flex*/"1.12", /*hexa*/"0x1.20p+0");
test_rtoa(buf, d, &dtoa, 2, /*scient*/"1.12e+00", /*flt*/"1.12", /*flex*/"1.12", /*hexa*/"0x1.20p+0");
test_rtoa(buf, f, &ftoa, 2, /*scient*/"1.12e+00", /*flt*/"1.12", /*flex*/"1.12", /*hexa*/"0x1.20p+0" _c4emscripten_alt("0x1.1f8p+0"));
test_rtoa(buf, d, &dtoa, 2, /*scient*/"1.12e+00", /*flt*/"1.12", /*flex*/"1.12", /*hexa*/"0x1.20p+0" _c4emscripten_alt("0x1.1f8p+0"));

test_rtoa(buf, f, &ftoa, 3, /*scient*/"1.123e+00", /*flt*/"1.123", /*flex*/"1.123", /*hexa*/"0x1.1f9p+0");
test_rtoa(buf, d, &dtoa, 3, /*scient*/"1.123e+00", /*flt*/"1.123", /*flex*/"1.123", /*hexa*/"0x1.1f9p+0");
test_rtoa(buf, f, &ftoa, 3, /*scient*/"1.123e+00", /*flt*/"1.123", /*flex*/"1.123", /*hexa*/"0x1.1f9p+0" _c4emscripten_alt("0x1.1f98p+0"));
test_rtoa(buf, d, &dtoa, 3, /*scient*/"1.123e+00", /*flt*/"1.123", /*flex*/"1.123", /*hexa*/"0x1.1f9p+0" _c4emscripten_alt("0x1.1f98p+0"));

test_rtoa(buf, f, &ftoa, 4, /*scient*/"1.1234e+00", /*flt*/"1.1234", /*flex*/"1.1234", /*hexa*/"0x1.1f98p+0");
test_rtoa(buf, d, &dtoa, 4, /*scient*/"1.1234e+00", /*flt*/"1.1234", /*flex*/"1.1234", /*hexa*/"0x1.1f98p+0");
Expand All @@ -1226,8 +1238,8 @@ TEST_CASE("ftoa.basic")
test_rtoa(buf, f, &ftoa, 2, /*scient*/"1.01e+00", /*flt*/"1.01", /*flex*/"1.01", /*hexa*/"0x1.03p+0");
test_rtoa(buf, d, &dtoa, 2, /*scient*/"1.01e+00", /*flt*/"1.01", /*flex*/"1.01", /*hexa*/"0x1.03p+0");

test_rtoa(buf, f, &ftoa, 3, /*scient*/"1.012e+00", /*flt*/"1.012", /*flex*/"1.012", /*hexa*/"0x1.033p+0", /*hexa*/"0x1.032p+0");
test_rtoa(buf, d, &dtoa, 3, /*scient*/"1.012e+00", /*flt*/"1.012", /*flex*/"1.012", /*hexa*/"0x1.033p+0", /*hexa*/"0x1.032p+0");
test_rtoa(buf, f, &ftoa, 3, /*scient*/"1.012e+00", /*flt*/"1.012", /*flex*/"1.012", /*hexa*/"0x1.033p+0" _c4emscripten_alt2("0x1.032p+0", "0x1.0328p+0"));
test_rtoa(buf, d, &dtoa, 3, /*scient*/"1.012e+00", /*flt*/"1.012", /*flex*/"1.012", /*hexa*/"0x1.033p+0" _c4emscripten_alt2("0x1.032p+0", "0x1.0328p+0"));

test_rtoa(buf, f, &ftoa, 4, /*scient*/"1.0123e+00", /*flt*/"1.0123", /*flex*/"1.0123", /*hexa*/"0x1.0329p+0");
test_rtoa(buf, d, &dtoa, 4, /*scient*/"1.0123e+00", /*flt*/"1.0123", /*flex*/"1.0123", /*hexa*/"0x1.0329p+0");
Expand Down