Skip to content

Commit ed827a4

Browse files
committed
Full support for RE2 and Abseil!
- Simpler Docker container - Single Makefile - Support for -flto - Proto upgrade to v26.1 - Updated build docs Signed-off-by: Martijn Stevenson <[email protected]>
1 parent df8faa9 commit ed827a4

17 files changed

+17902
-17848
lines changed

Dockerfile-sdk

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
FROM ubuntu:noble
22

3-
COPY ./sdk_container.sh /
4-
COPY ./build_wasm.sh /
3+
COPY *.sh /
54
COPY *.cc *.h *.js *.proto Makefile* /sdk/
65

76
RUN ./sdk_container.sh

Makefile

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,57 @@
1-
CPP_API ?= .
1+
ifdef NO_CONTEXT
2+
CPP_CONTEXT_LIB =
3+
else
4+
CPP_CONTEXT_LIB = ${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics.cc
5+
endif
26

3-
all: proxy_wasm_intrinsics.pb.h proxy_wasm_intrinsics_lite.pb.h struct_lite.pb.h ${CPP_API}/libprotobuf.a ${CPP_API}/libprotobuf-lite.a
7+
PROTOBUF?=none
8+
ifeq ($(PROTOBUF), full)
9+
PROTO_DEPS := protobuf
10+
PROTO_OPTS := -DPROXY_WASM_PROTOBUF_FULL=1 \
11+
${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics.pb.cc
12+
else ifeq ($(PROTOBUF), lite)
13+
PROTO_DEPS := protobuf-lite
14+
PROTO_OPTS := -DPROXY_WASM_PROTOBUF_LITE=1 \
15+
${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics_lite.pb.cc \
16+
${PROXY_WASM_CPP_SDK}/struct_lite.pb.cc
17+
else
18+
PROTO_DEPS :=
19+
PROTO_OPTS :=
20+
endif
421

5-
protobuf: proxy_wasm_intrinsics.pb.h proxy_wasm_intrinsics_lite.pb.h struct_lite.pb.h
22+
# Provide a list of libraries that the wasm depends on (absl_*, re2, etc).
23+
WASM_DEPS?=absl_base
624

7-
proxy_wasm_intrinsics.pb.h: proxy_wasm_intrinsics.proto
8-
protoc --cpp_out=. proxy_wasm_intrinsics.proto
25+
# Determine dependency link options.
26+
# NOTE: Strip out -pthread which RE2 claims to need...
27+
PKG_CONFIG?=pkg-config
28+
PKG_CONFIG_PATH=${EMSDK}/upstream/emscripten/cache/sysroot/lib/pkgconfig
29+
WASM_LIBS=$(shell $(PKG_CONFIG) $(WASM_DEPS) $(PROTO_DEPS) \
30+
--with-path=$(PKG_CONFIG_PATH) --libs | sed -e 's/-pthread //g')
931

10-
proxy_wasm_intrinsics_lite.pb.h struct_lite.pb.h: proxy_wasm_intrinsics_lite.proto
11-
protoc --cpp_out=. -I. proxy_wasm_intrinsics_lite.proto
12-
protoc --cpp_out=. struct_lite.proto
32+
debug-deps:
33+
# WASM_DEPS : ${WASM_DEPS}
34+
# WASM_LIBS : ${WASM_LIBS}
35+
# PROTO_DEPS: ${PROTO_DEPS}
36+
# PROTO_OPTS: ${PROTO_OPTS}
1337

14-
${CPP_API}/libprotobuf.a ${CPP_API}/libprotobuf-lite.a:
15-
rm -rf protobuf-wasm \
16-
&& git clone https://github.com/protocolbuffers/protobuf protobuf-wasm \
17-
&& cd protobuf-wasm \
18-
&& git checkout v3.9.1 \
19-
&& ./autogen.sh \
20-
&& emconfigure ./configure --disable-shared CXXFLAGS="-O3 -flto" \
21-
&& emmake make \
22-
&& cd .. \
23-
&& cp protobuf-wasm/src/.libs/libprotobuf-lite.a ${CPP_API}/libprotobuf-lite.a \
24-
&& cp protobuf-wasm/src/.libs/libprotobuf.a ${CPP_API}/libprotobuf.a
38+
# TODO(martijneken): Add Emscripten stack/heap size params.
39+
%.wasm %.wat: %.cc
40+
em++ --no-entry -sSTANDALONE_WASM -sEXPORTED_FUNCTIONS=_malloc \
41+
--std=c++17 -O3 -flto \
42+
--js-library ${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics.js \
43+
-I${PROXY_WASM_CPP_SDK} \
44+
${CPP_CONTEXT_LIB} \
45+
${PROTO_OPTS} \
46+
${WASM_LIBS} \
47+
$*.cc -o $*.wasm
2548

2649
clean:
27-
rm -f proxy_wasm_intrinsics.pb.h proxy_wasm_intrinsics_lite.pb.h struct_lite.pb.h ${CPP_API}/libprotobuf.a ${CPP_API}/libprotobuf-lite.a
50+
rm *.wasm
51+
52+
# NOTE: How to regenerate .pb.h and .pb.cc files for a protobuf update:
53+
# - download + extract protobuf release (currently v26.1)
54+
# - regenerate:
55+
# ./bin/protoc --cpp_out=../ -I../ -Iinclude/google/protobuf/ ../struct_lite.proto
56+
# ./bin/protoc --cpp_out=../ -I../ -Iinclude/google/protobuf/ ../proxy_wasm_intrinsics_lite.proto
57+
# ./bin/protoc --cpp_out=../ -I../ -Iinclude/google/protobuf/ ../proxy_wasm_intrinsics.proto

Makefile.base

Lines changed: 0 additions & 11 deletions
This file was deleted.

Makefile.base_lite

Lines changed: 0 additions & 11 deletions
This file was deleted.

bazel/defs.bzl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@ load("@emsdk//emscripten_toolchain:wasm_rules.bzl", "wasm_cc_binary")
1616
load("@rules_cc//cc:defs.bzl", "cc_binary")
1717

1818
def _optimized_wasm_cc_binary_transition_impl(settings, attr):
19-
# TODO(PiotrSikora): Add -flto to copts/linkopts when fixed in emsdk.
20-
# See: https://github.com/emscripten-core/emsdk/issues/971
21-
#
2219
# Define STANDALONE_WASM at compile time as well as link time (below).
2320
# This fixes Abseil by not including Emscripten JS stacktraces + symbolization.
2421
# TODO(martijneken): Remove after Abseil stops using this define.
2522
return {
26-
"//command_line_option:copt": ["-O3", "-DSTANDALONE_WASM"],
23+
"//command_line_option:copt": ["-O3", "-flto", "-DSTANDALONE_WASM"],
2724
"//command_line_option:cxxopt": [],
2825
"//command_line_option:linkopt": [],
2926
"//command_line_option:collect_code_coverage": False,
@@ -95,6 +92,7 @@ def proxy_wasm_cc_binary(
9592
linkopts = linkopts + [
9693
"--no-entry",
9794
"--js-library=$(location @proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics_js)",
95+
"-sSTANDALONE_WASM",
9896
"-sEXPORTED_FUNCTIONS=_malloc",
9997
],
10098
tags = tags + [

build_wasm.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616

1717
source /root/emsdk/emsdk_env.sh
1818
export PATH=/usr/local/bin:$PATH
19-
make
19+
make "$@"

docs/building.md

Lines changed: 32 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Proxy-Wasm C++ SDK Build Instructions
22

33
The C++ SDK has dependencies on specific versions of the C++ WebAssembly
4-
toolchain [Emscripten](https://emscripten.org) and the protobuf library,
5-
therefore use of a Docker image is recommended.
4+
toolchain [Emscripten](https://emscripten.org). Use of a Docker image is
5+
recommended to achieve repeatable builds and save work.
66

77
## Docker
88

@@ -11,7 +11,7 @@ A Dockerfile for the C++ SDK is provided in [Dockerfile-sdk](../Dockerfile-sdk).
1111
It can built in this repository's root directory by:
1212

1313
```bash
14-
docker build -t wasmsdk:v2 -f Dockerfile-sdk .
14+
docker build -t wasmsdk:v3 -f Dockerfile-sdk .
1515
```
1616

1717
The docker image can be used for compiling C++ plugin code into Wasm modules.
@@ -23,12 +23,10 @@ Create a directory with your source files and a Makefile:
2323
```makefile
2424
PROXY_WASM_CPP_SDK=/sdk
2525

26-
all: myproject.wasm
27-
28-
include ${PROXY_WASM_CPP_SDK}/Makefile.base_lite
26+
include ${PROXY_WASM_CPP_SDK}/Makefile
2927
```
3028

31-
Source file (myproject.cc):
29+
Create a C++ source file (myproject.cc):
3230

3331
```c++
3432
#include <string>
@@ -57,10 +55,26 @@ void ExampleContext::onDone() { logInfo("onDone " + std::to_string(id())); }
5755

5856
### Compiling with the Docker build image
5957

60-
Run docker:
58+
Run docker to build wasm, using a target with a .wasm suffix:
6159

6260
```bash
63-
docker run -v $PWD:/work -w /work wasmsdk:v2 /build_wasm.sh
61+
docker run -v $PWD:/work -w /work wasmsdk:v3 /build_wasm.sh myproject.wasm
62+
```
63+
64+
You can specify wasm dependencies via these Makefile variables:
65+
66+
- PROTOBUF = {full, lite, none}
67+
- WASM_DEPS = list of libraries
68+
69+
For example:
70+
71+
```makefile
72+
PROXY_WASM_CPP_SDK=/sdk
73+
74+
PROTOBUF=lite
75+
WASM_DEPS=re2 absl_strings
76+
77+
include ${PROXY_WASM_CPP_SDK}/Makefile
6478
```
6579

6680
### Caching the standard libraries
@@ -70,7 +84,7 @@ cache these in the docker image, after the first successful compilation (e.g
7084
myproject.cc above), commit the image with the standard libraries:
7185

7286
```bash
73-
docker commit `docker ps -l | grep wasmsdk:v2 | awk '{print $1}'` wasmsdk:v2
87+
docker commit `docker ps -l | grep wasmsdk:v3 | awk '{print $1}'` wasmsdk:v3
7488
```
7589

7690
This will save time on subsequent compiles.
@@ -86,48 +100,32 @@ PROXY_WASM_CPP_SDK=/sdk
86100
all: myproject.wasm
87101
chown ${uid}.${gid} $^
88102

89-
include ${PROXY_WASM_CPP_SDK}/Makefile.base_lite
103+
include ${PROXY_WASM_CPP_SDK}/Makefile
90104
```
91105

92106
Invocation file (e.g. build.sh):
93107

94108
```bash
95109
#!/bin/bash
96-
docker run -e uid="$(id -u)" -e gid="$(id -g)" -v $PWD:/work -w /work wasmsdk:v2 /build_wasm.sh
110+
docker run -e uid="$(id -u)" -e gid="$(id -g)" -v $PWD:/work -w /work wasmsdk:v3 /build_wasm.sh
97111
```
98112

99113
## Dependencies for building Wasm modules:
100114

101115
If you do not wish to use the Docker image, the dependencies can be installed by
102-
script (sdk\_container.sh), or by hand.
103-
104-
### protobuf v3.9.1
105-
106-
You must install the version of protobuf on your build system that matches the
107-
libprotobuf.a files (without any patches) so that the generated code matches the
108-
.a library. Currently this is based on tag v3.9.1 of
109-
https://github.com/protocolbuffers/protobuf.
110-
111-
```bash
112-
git clone https://github.com/protocolbuffers/protobuf
113-
cd protobuf
114-
git checkout v3.9.1
115-
git submodule update --init --recursive
116-
./autogen.sh
117-
./configure
118-
make
119-
make check
120-
sudo make install
121-
```
116+
script (sdk\_container.sh), or by hand. First you need Emscripten to build
117+
everything else. Then you can build other wasm-compatible libraries, such as
118+
protobuf, abseil, and RE2.
122119

123120
### Emscripten
124121

122+
Version 3.1.67 is known to work:
123+
125124
```bash
126125
git clone https://github.com/emscripten-core/emsdk.git
127126
cd emsdk
128127
git checkout 3.1.67
129-
130-
./emsdk install 3.1.67
128+
./emsdk install --shallow 3.1.67
131129
./emsdk activate 3.1.67
132130

133131
source ./emsdk_env.sh
@@ -140,26 +138,3 @@ It is possible later versions will work, e.g.
140138
./emsdk install latest
141139
./emsdk activate latest
142140
```
143-
144-
However 3.1.67 is known to work.
145-
146-
### Rebuilding the libprotobuf.a files
147-
148-
To build the protobuf static libraries for use in your proxy-wasm wasm module,
149-
if you need them, you may use Emscripten in the same way sdk\_container.sh does
150-
it:
151-
152-
```bash
153-
git clone https://github.com/protocolbuffers/protobuf protobuf-wasm
154-
cd protobuf-wasm
155-
git checkout v3.9.1
156-
git submodule update --init --recursive
157-
./autogen.sh
158-
emconfigure ./configure --disable-shared CXXFLAGS="-O3 -flto"
159-
emmake make
160-
cd ..
161-
cp protobuf-wasm/src/.libs/libprotobuf-lite.a ${CPP_API}/libprotobuf-lite.a
162-
cp protobuf-wasm/src/.libs/libprotobuf.a ${CPP_API}/libprotobuf.a
163-
```
164-
165-
Note: ensure /usr/local/bin is in your path.

example/http_proto_example.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "proxy_wasm_intrinsics.h"
16+
17+
class MyRootContext : public RootContext {
18+
public:
19+
explicit MyRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {}
20+
21+
bool onStart(size_t) override {
22+
#if defined(PROXY_WASM_PROTOBUF_FULL)
23+
LOG_TRACE("onStart with protobuf (full)");
24+
google::protobuf::Value value;
25+
value.set_string_value("unused");
26+
#elif defined(PROXY_WASM_PROTOBUF_LITE)
27+
LOG_TRACE("onStart with protobuf (lite)");
28+
google::protobuf::Value value;
29+
value.set_string_value("unused");
30+
#else
31+
LOG_TRACE("onStart without protobuf");
32+
#endif
33+
return true;
34+
}
35+
};
36+
37+
class MyHttpContext : public Context {
38+
public:
39+
explicit MyHttpContext(uint32_t id, RootContext *root) : Context(id, root) {}
40+
};
41+
42+
static RegisterContextFactory register_StaticContext(CONTEXT_FACTORY(MyHttpContext),
43+
ROOT_FACTORY(MyRootContext));

example/http_re_example.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "proxy_wasm_intrinsics.h"
16+
#include "re2/re2.h"
17+
18+
class MyRootContext : public RootContext {
19+
public:
20+
explicit MyRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {}
21+
22+
bool onConfigure(size_t) override {
23+
// Compile the regex expression at plugin setup time.
24+
path_match.emplace("/foo-([^/]+)/");
25+
return path_match->ok();
26+
}
27+
28+
std::optional<re2::RE2> path_match;
29+
};
30+
31+
class MyHttpContext : public Context {
32+
public:
33+
explicit MyHttpContext(uint32_t id, RootContext *root)
34+
: Context(id, root), root_(static_cast<MyRootContext *>(root)) {}
35+
36+
FilterHeadersStatus onRequestHeaders(uint32_t headers, bool end_of_stream) override {
37+
auto path = getRequestHeader(":path");
38+
if (path) {
39+
std::string edit = path->toString(); // mutable copy
40+
if (re2::RE2::Replace(&edit, *root_->path_match, "/\\1/")) {
41+
replaceRequestHeader(":path", edit);
42+
}
43+
}
44+
return FilterHeadersStatus::Continue;
45+
}
46+
47+
private:
48+
const MyRootContext *root_;
49+
};
50+
51+
static RegisterContextFactory register_StaticContext(CONTEXT_FACTORY(MyHttpContext),
52+
ROOT_FACTORY(MyRootContext));

0 commit comments

Comments
 (0)