Bazel target compiles in gcc but fails in emscripten

700 Views Asked by At

I am building a system using Bazel where I have a C++ target and an emscirpten (emcc) target to compile to web assembly. Both depend on "myAPI", which has external dependencies libpng, libz, opencv and some others.

This compiles properly:

# Development
cc_binary(
    name = "devMain",
    srcs = ["dev/main.cpp"],
    copts = [
        "-w",
        "-g",
        "-O3",
        "--std=c++2a",
        "-pthread",
        "-Iinclude/",
        "-Iexternal/geometryCentralRepo/include",
        "-Iexternal/eigenRepo",
        "-DBUILD_VERSION=devMain",
        ],
    deps = [
        ":myAPI",
    ],
    linkopts = ["-lpthread"],
)

However, this fails:

DEFAULT_EMSCRIPTEN_LINKOPTS = [
    "-s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']",
    "-s ALLOW_MEMORY_GROWTH=1",     # Our example doesn't need memory growth
    "-s ASSERTIONS=1",              # Turn off assertions
    "-s EXPORT_ES6=1",              # Export as es6 module, used for rollup
    "-s MODULARIZE=1",              # Allows us to manually invoke the initialization of wasm
    "-s EXPORT_NAME=myAPI",    # Not used, but good to specify
    "-s USE_ES6_IMPORT_META=0",     # Disable loading from import meta since we use rollup
    "-s EXPORT_ALL=1",
    "-s USE_PTHREADS=1",
    "-s ENVIRONMENT=worker",
    "-DNDEBUG",
]

WASM_LINKOPTS = [
    "-s WASM=1",
    "--bind",                    # Specify wasm output
]

cc_binary(
    name = "myAPI.js",
    srcs=["src/myAPI/WASM_bindings.cpp"],
    copts = [
        "-g",
        "-O3",
        "--std=c++2a",
        "-pthread",
        "-Iinclude/",
        ],
    linkopts = DEFAULT_EMSCRIPTEN_LINKOPTS + WASM_LINKOPTS,
    deps = [
        ":myAPI",
    ],

)

wasm_cc_binary(
    name = "setupModel-wasm",        
    cc_target = ":myAPI.js",
)

Here is myAPI:

cc_library(
    name = "myAPI",
    srcs = glob(["src/**/*.c*"], exclude=["src/myAPI/WASM_bindings.cpp"]),
    hdrs = glob(["include/**/*.h*"]),
    copts = [
        "-w",
        "-g",
        "-O3",
        "--std=c++2a",
        "-pthread",
        "-Iinclude",
        "-Iexternal/geometryCentralRepo/include",
        "-Iexternal/spdlogRepo/include",
        "-Iexternal/libiglRepo/include", 
        "-Iexternal/openCVRepo/include", 
        "-Iexternal/eigenRepo",
        ],
    deps = [
        "@eigenRepo//:eigen",
        "@libiglRepo//:libigl",
        "@geometryCentralRepo//:geoCentral",
        "@openCVRepo//:opencv_core",
        "@openCVRepo//:opencv_imgcodecs",
        "@spdlogRepo//:spdlog",
        "@libpngRepo//:libpng"
    ],      
    visibility = ["//visibility:public"],
)

Finally, these are all the dependencies:

eigen:

load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "eigen",
    hdrs =  glob(["Eigen/**", "unsupported/**"]),
    copts = ["-w", "-O3", "-pthread"],
    visibility = ["//visibility:public"],
)

libigl:

load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "libigl",
    hdrs =  glob(["**/*.h*", "**/*.c*"]),
    copts = ["-w", "-O3", "-pthread"],
    visibility = ["//visibility:public"],
)

geoCentral:

load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "geoCentral",
    srcs = glob(["src/**/*.cpp"], 
    exclude=["src/utilities/unit_vector3.cpp", "src/surface/mesh_ray_tracer.cpp", "src/surface/detect_symmetry.cpp"]),
    hdrs = glob(["**/*.h", "**/*.ipp"]),
    copts = [
    "-w",
    "-O3",
    "-pthread",
    "-Iexternal/geometryCentralRepo/include", 
    "-Iexternal/geometryCentralRepo/deps", 
    "-Iexternal/geometryCentralRepo/src",
    "-Iexternal/geometryCentralRepo",
    "-Iexternal/geometryCentralRepo/deps/nanort/include",
    "-Iexternal/geometryCentralRepo/deps/happly", 
    "-Iexternal/eigenRepo", 
    "-Iexternal/nanoflann",
    "-Iexternal",
    "-Iexternal/happlyRepo/"],
    deps = ["@eigenRepo//:eigen", "@nanoflann//:nanoflann", "@happlyRepo//:happly"],
    visibility = ["//visibility:public"],
)

openCV:

load("@rules_cc//cc:defs.bzl", "cc_library")

package(default_visibility = ["//visibility:private"])


cc_library(
    name = "opencv_core",
    srcs = glob([
        "modules/core/src/**/*.cpp",
        "modules/core/src/**/*.hpp",
        "modules/core/include/**/*.hpp",
        "modules/core/include/**/*.h",
    ]) + [
        "custom_hal.hpp",
        "cvconfig.h",
        "opencl_kernels_core.hpp",
        "opencv2/opencv_modules.hpp",
        "version_string.inc",
    ],
    hdrs = ["modules/core/include/opencv2/core/opencl/ocl_defs.hpp"],
    copts = [
        "-D__OPENCV_BUILD",
        "-Iexternal/zlib",
    ],
    includes = [
        ".",
        "modules/core/include",
    ],
    linkopts = [
        "-ldl",
    ] + select({
        ":arm": ["-llog"],
        "//conditions:default": ["-lpthread"],
    }),
    visibility = ["//visibility:public"],
    deps = [
        "@zlibRepo//:zlib",
    ],
)

genrule(
    name = "opencv_core_kernels",
    outs = ["opencl_kernels_core.hpp"],
    cmd = """
      echo '#include "opencv2/core/ocl.hpp"' > $@
      echo '#include "opencv2/core/ocl_genbase.hpp"' > $@
      echo '#include "opencv2/core/opencl/ocl_defs.hpp"' > $@
    """,
)

genrule(
    name = "cvconfig",
    srcs = select({
        ":arm": ["cvconfig_android.h"],
        "//conditions:default": ["cvconfig_linux.h"],
    }),
    outs = ["cvconfig.h"],
    cmd = "cp $< $@",
)

genrule(
    name = "cvconfig_linux",
    outs = ["cvconfig_linux.h"],
    cmd = """
      echo '#define HAVE_PNG' >> $@
    """,
)

genrule(
    name = "cvconfig_android",
    outs = ["cvconfig_android.h"],
    cmd = """
      echo '#define HAVE_PNG' >> $@
      echo '#define ANDROID' >> $@
    """,
)

genrule(
    name = "custom_hal",
    outs = ["custom_hal.hpp"],
    cmd = "touch $@",
)

genrule(
    name = "version_string",
    outs = ["version_string.inc"],
    cmd = "echo '\"OpenCV 3.1.0\"' > $@",
)

genrule(
    name = "opencv_modules",
    outs = ["opencv2/opencv_modules.hpp"],
    cmd = """
        echo '#define HAVE_OPENCV_CORE' >> $@
        echo '#define HAVE_OPENCV_IMGCODECS' >> $@
        echo '#define HAVE_OPENCV_IMGPROC' >> $@
        echo '#define HAVE_OPENCV_ML' >> $@
        echo '#define HAVE_OPENCV_VIDEOIO' >> $@
    """,
)

cc_library(
    name = "opencv_imgproc",
    srcs = glob([
        "modules/imgproc/src/**/*.cpp",
        "modules/imgproc/src/**/*.hpp",
        "modules/imgproc/src/**/*.h",
        "modules/imgproc/include/**/*.hpp",
        "modules/imgproc/include/**/*.h",
    ]) + ["opencl_kernels_imgproc.hpp"],
    copts = ["-D__OPENCV_BUILD"],
    includes = [
        ".",
        "modules/core/include",
        "modules/imgproc/include",
    ],
    visibility = ["//visibility:public"],
    deps = [":opencv_core"],
)

genrule(
    name = "opencv_imgproc_kernels",
    outs = ["opencl_kernels_imgproc.hpp"],
    cmd = """
      echo '#include "opencv2/core/ocl.hpp"' > $@
      echo '#include "opencv2/core/ocl_genbase.hpp"' > $@
      echo '#include "opencv2/core/opencl/ocl_defs.hpp"' > $@
    """,
)

cc_library(
    name = "opencv_ml",
    srcs = glob([
        "modules/ml/src/**/*.cpp",
        "modules/ml/src/**/*.hpp",
        "modules/ml/include/**/*.hpp",
    ]),
    copts = ["-D__OPENCV_BUILD"],
    includes = ["modules/ml/include"],
    visibility = ["//visibility:public"],
    deps = [":opencv_core"],
)

cc_library(
    name = "opencv_imgcodecs",
    srcs = glob([
        "modules/imgcodecs/src/**/*.cpp",
        "modules/imgcodecs/src/**/*.hpp",
        "modules/imgcodecs/include/**/*.hpp",
        "modules/imgcodecs/include/**/*.h",
    ]),
    copts = [
        "-D__OPENCV_BUILD",
        "-Iexternal/libpng_http",
        "-Iexternal/libpng",
        "-Iexternal/zlib",
    ],
    includes = [
        "modules/imgcodecs/include",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":opencv_core",
        ":opencv_imgproc",
        "@libpngRepo//:libpng",
        "@zlibRepo//:zlib",
    ],
)

spdLog:

load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "spdlog",
    hdrs = glob(["include/**/*.h"]),
    includes = ["include"],
    visibility = ["//visibility:public"],
)

libpng:

load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "libpng",
    srcs = glob(["*.c"]), 
    hdrs = glob(["*.h"]),
    copts = [
    "-w",
    "-O3",
    "-pthread",
    "-Iexternal",
    ],
    includes = [
        ".",
    ],
    linkopts = [
        "-lpng", 
        "-lz",
    ],
    visibility = ["//visibility:public"],
    deps = [
        "@zlibRepo//:zlib",
    ],
)

zLib:

load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
    name = "zlib",
    srcs = glob(["*.cpp"]), 
    hdrs = glob(["*.h"]),
    copts = [
    "-w",
    "-O3",
    "-pthread",
    "-Iexternal",
    ],
    visibility = ["//visibility:public"],
)

I am getting a bunch of include errors, like: fatal error: 'pnglibconf.h' file not found

As far as I understand, this is a linker issue, where 'pnglibconf.h' should have been created by the compiler and compiled into a library that the linker should be able to link.

What is particularly strange is that ":myAPI", which is the only dependency, compiles properly with g++, so it must be a matter of setting the correct linkoptions?

1

There are 1 best solutions below

0
On

I finally reached a solution for this:

Unsurprisingly, there were more than one issues, including the following:

  1. Emscripten does not like the c++2a standard when compiling the libpng version that was shipped together with openCV 3.1.0 that I am using. Thus, I ended up compiling with -std=c++2a using g++ and without it when compiling with emcc (emscripten).

  2. Something similar holds for the linker options as well, where g++ requires -lpng -lz (in this order), while emcc requires nothing.

  3. Also, emscripten requires a linker flag when compiling zlib (which is a dependency in my case): -s USE_ZLIB=1.

I hope this helps, good luck with your project too...