Add pecl support. (#8)

Co-authored-by: 何延龙 <hey.yanlong@gmail.com>
Co-authored-by: 吴晟 Wu Sheng <wu.sheng@foxmail.com>
diff --git a/.github/workflows/pecl.yml b/.github/workflows/pecl.yml
new file mode 100644
index 0000000..601d71e
--- /dev/null
+++ b/.github/workflows/pecl.yml
@@ -0,0 +1,75 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: PECL
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - "**"
+
+env:
+  CARGO_TERM_COLOR: always
+  RUST_BACKTRACE: "1"
+  RUSTFLAGS: "-D warnings"
+  LLVM_CONFIG_PATH: llvm-config-10
+  RUSTUP_HOME: /opt/rustup
+  CARGO_HOME: /opt/cargo
+
+jobs:
+  pecl:
+    name: PECL
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - ubuntu-20.04
+        version:
+          - php: "8.1"
+            swoole: "5.0.0"
+
+    runs-on: ${{ matrix.os }}
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          submodules: 'recursive'
+
+      - name: Install libclang
+        run: sudo apt-get install -y llvm-10-dev libclang-10-dev protobuf-compiler
+
+      - name: Setup PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.version.php }}
+          tools: php-config
+          extensions: swoole-${{ matrix.version.swoole }}
+
+      - name: Install Rust Stable Globally
+        run: |
+          curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path
+          ln -s $CARGO_HOME/bin/rustup /usr/local/bin/rustup
+          ln -s $CARGO_HOME/bin/rustc /usr/local/bin/rustc
+          ln -s $CARGO_HOME/bin/cargo /usr/local/bin/cargo
+
+      - name: PECL install
+        run: |
+          sudo rustup default stable
+          sudo cargo run -p scripts --release -- create-package-xml --version 0.0.0 --notes "Just for TEST."
+          printf "\n" | sudo pecl install package.xml
+          php -d "extension=skywalking_agent" --ri skywalking_agent
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 2869ee4..ce898e1 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -62,7 +62,7 @@
           submodules: 'recursive'
 
       - name: Install libclang
-        run: sudo apt-get install -y llvm-10-dev libclang-10-dev
+        run: sudo apt-get install -y llvm-10-dev libclang-10-dev protobuf-compiler
 
       - name: Setup PHP
         uses: shivammathur/setup-php@v2
@@ -98,11 +98,25 @@
           command: clippy
           args: --release
 
+      - name: Cargo build
+        uses: actions-rs/cargo@v1
+        with:
+          toolchain: stable
+          command: build
+          args: --release
+
       - name: Docker compose
         run: docker compose up -d
 
-      - name: Make test
-        run: make test
+      - name: Composer install
+        run: composer install --working-dir=tests/php
+
+      - name: Cargo test
+        uses: actions-rs/cargo@v1
+        with:
+          toolchain: stable
+          command: test
+          args: --release
 
   fmt:
     name: Fmt
diff --git a/.gitignore b/.gitignore
index de6dc9c..b885819 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,47 @@
 /.cargo/config.toml
 # PHP composer vendor.
 /tests/php/vendor/
+
+/skywalking_agent-*.tgz
+/package.xml
+
+*.lo
+*.la
+.libs
+acinclude.m4
+aclocal.m4
+autom4te.cache
+build
+config.guess
+config.h
+config.h.in
+config.h.in~
+config.log
+config.nice
+config.status
+config.sub
+configure
+configure.ac
+configure.in
+include
+install-sh
+libtool
+ltmain.sh
+Makefile
+Makefile.fragments
+Makefile.global
+Makefile.objects
+missing
+mkinstalldirs
+modules
+php_test_results_*.txt
+phpt.*
+run-test-info.php
+run-tests.php
+tests/**/*.diff
+tests/**/*.out
+tests/**/*.exp
+tests/**/*.log
+tests/**/*.db
+tests/**/*.mem
+tmp-php.ini
diff --git a/.licenserc.yaml b/.licenserc.yaml
index 3da6577..fbbda5d 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -19,17 +19,18 @@
     copyright-owner: Apache Software Foundation
 
   paths-ignore:
-    - 'LICENSE'
-    - 'NOTICE'
-    - '**/*.md'
     - '**/*.json'
     - '**/*.lock'
+    - '**/*.md'
     - '**/.gitignore'
     - '**/.gitmodules'
-    - 'vendor'
     - '.cargo'
-    - '.vscode'
     - '.idea'
+    - '.vscode'
+    - 'LICENSE'
+    - 'NOTICE'
+    - 'config.m4'
+    - 'vendor'
 
   comment: on-failure
 
diff --git a/Cargo.lock b/Cargo.lock
index 11b9444..3dc915b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.18"
+version = "0.7.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
 dependencies = [
  "memchr",
 ]
@@ -31,9 +31,9 @@
 
 [[package]]
 name = "anyhow"
-version = "1.0.63"
+version = "1.0.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26fa4d7e3f2eebadf743988fc8aec9fa9a9e82611acafd77c1462ed6262440a"
+checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
 
 [[package]]
 name = "async-stream"
@@ -85,15 +85,6 @@
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
-name = "autotools"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8138adefca3e5d2e73bfba83bd6eeaf904b26a7ac1b4a19892cfe16cc7e1701"
-dependencies = [
- "cc",
-]
-
-[[package]]
 name = "axum"
 version = "0.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -183,6 +174,24 @@
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "block-buffer"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bstr"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "bumpalo"
 version = "3.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -243,6 +252,28 @@
 ]
 
 [[package]]
+name = "chrono-tz"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c39203181991a7dd4343b8005bd804e7a9a37afb8ac070e43771e8c820bbde"
+dependencies = [
+ "chrono",
+ "chrono-tz-build",
+ "phf",
+]
+
+[[package]]
+name = "chrono-tz-build"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f509c3a87b33437b05e2458750a0700e5bdd6956176773e6c7d6dd15a283a0c"
+dependencies = [
+ "parse-zoneinfo",
+ "phf",
+ "phf_codegen",
+]
+
+[[package]]
 name = "clang-sys"
 version = "1.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -255,9 +286,9 @@
 
 [[package]]
 name = "clap"
-version = "3.2.19"
+version = "3.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68d43934757334b5c0519ff882e1ab9647ac0258b47c24c4f490d78e42697fd5"
+checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
 dependencies = [
  "atty",
  "bitflags",
@@ -315,6 +346,15 @@
 checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
 
 [[package]]
+name = "cpufeatures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "crossbeam-channel"
 version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -335,6 +375,16 @@
 ]
 
 [[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
 name = "dashmap"
 version = "5.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -367,6 +417,22 @@
 ]
 
 [[package]]
+name = "deunicode"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690"
+
+[[package]]
+name = "digest"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
 name = "either"
 version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -538,6 +604,16 @@
 ]
 
 [[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
 name = "getrandom"
 version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -566,6 +642,30 @@
 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
 [[package]]
+name = "globset"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "fnv",
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "globwalk"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
+dependencies = [
+ "bitflags",
+ "ignore",
+ "walkdir",
+]
+
+[[package]]
 name = "h2"
 version = "0.3.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -666,6 +766,12 @@
 checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
 
 [[package]]
+name = "humansize"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
+
+[[package]]
 name = "humantime"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -746,6 +852,24 @@
 ]
 
 [[package]]
+name = "ignore"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
+dependencies = [
+ "crossbeam-utils",
+ "globset",
+ "lazy_static",
+ "log",
+ "memchr",
+ "regex",
+ "same-file",
+ "thread_local",
+ "walkdir",
+ "winapi-util",
+]
+
+[[package]]
 name = "indexmap"
 version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1069,9 +1193,9 @@
 
 [[package]]
 name = "once_cell"
-version = "1.13.1"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
+checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
 
 [[package]]
 name = "openssl"
@@ -1148,6 +1272,15 @@
 ]
 
 [[package]]
+name = "parse-zoneinfo"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
+dependencies = [
+ "regex",
+]
+
+[[package]]
 name = "peeking_take_while"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1160,6 +1293,50 @@
 checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
+name = "pest"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4"
+dependencies = [
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "905708f7f674518498c1f8d644481440f476d39ca6ecae83319bba7c6c12da91"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5803d8284a629cc999094ecd630f55e91b561a1d1ba75e233b00ae13b91a69ad"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha-1",
+]
+
+[[package]]
 name = "petgraph"
 version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1170,6 +1347,45 @@
 ]
 
 [[package]]
+name = "phf"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
+dependencies = [
+ "phf_shared",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
+dependencies = [
+ "siphasher",
+ "uncased",
+]
+
+[[package]]
 name = "phper"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1268,9 +1484,9 @@
 
 [[package]]
 name = "portable-atomic"
-version = "0.3.13"
+version = "0.3.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b303a15aeda678da614ab23306232dbd282d532f8c5919cedd41b66b9dc96560"
+checksum = "e27d61145bec6e13ec8e61feeb490d38de01c2346a804668d036a518a461c0de"
 
 [[package]]
 name = "ppv-lite86"
@@ -1280,9 +1496,9 @@
 
 [[package]]
 name = "prettyplease"
-version = "0.1.18"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "697ae720ee02011f439e0701db107ffe2916d83f718342d65d7f8bf7b8a5fee9"
+checksum = "a49e86d2c26a24059894a3afa13fd17d063419b05dfb83f06d9c3566060c3f5a"
 dependencies = [
  "proc-macro2",
  "syn",
@@ -1375,15 +1591,6 @@
 ]
 
 [[package]]
-name = "protobuf-src"
-version = "1.0.5+3.19.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe57f68bf9767f48f8cbcbceb5da21524e2b1330a821c1c2502c447d8043f078"
-dependencies = [
- "autotools",
-]
-
-[[package]]
 name = "quick-error"
 version = "1.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1575,6 +1782,15 @@
 checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
 
 [[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
 name = "schannel"
 version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1591,6 +1807,19 @@
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
+name = "scripts"
+version = "0.0.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "clap",
+ "serde",
+ "tera",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
 name = "security-framework"
 version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1663,6 +1892,17 @@
 ]
 
 [[package]]
+name = "sha-1"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
+dependencies = [
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
 name = "sharded-slab"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1687,6 +1927,12 @@
 ]
 
 [[package]]
+name = "siphasher"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
+
+[[package]]
 name = "skywalking"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1701,7 +1947,6 @@
  "portable-atomic",
  "prost",
  "prost-derive",
- "protobuf-src",
  "serde",
  "thiserror",
  "tokio",
@@ -1750,6 +1995,15 @@
 ]
 
 [[package]]
+name = "slug"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373"
+dependencies = [
+ "deunicode",
+]
+
+[[package]]
 name = "smallvec"
 version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1757,9 +2011,9 @@
 
 [[package]]
 name = "socket2"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10c98bba371b9b22a71a9414e420f92ddeb2369239af08200816169d5e2dd7aa"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
 dependencies = [
  "libc",
  "winapi 0.3.9",
@@ -1817,6 +2071,28 @@
 ]
 
 [[package]]
+name = "tera"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d4685e72cb35f0eb74319c8fe2d3b61e93da5609841cde2cb87fcc3bea56d20"
+dependencies = [
+ "chrono",
+ "chrono-tz",
+ "globwalk",
+ "humansize",
+ "lazy_static",
+ "percent-encoding",
+ "pest",
+ "pest_derive",
+ "rand 0.8.5",
+ "regex",
+ "serde",
+ "serde_json",
+ "slug",
+ "unic-segment",
+]
+
+[[package]]
 name = "termcolor"
 version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1833,18 +2109,18 @@
 
 [[package]]
 name = "thiserror"
-version = "1.0.33"
+version = "1.0.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57"
+checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.33"
+version = "1.0.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09"
+checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1899,9 +2175,9 @@
 
 [[package]]
 name = "tokio"
-version = "1.20.1"
+version = "1.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
+checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42"
 dependencies = [
  "autocfg",
  "bytes",
@@ -2202,6 +2478,77 @@
 checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
 [[package]]
+name = "typenum"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
+
+[[package]]
+name = "uncased"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unic-char-property"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
+dependencies = [
+ "unic-char-range",
+]
+
+[[package]]
+name = "unic-char-range"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
+
+[[package]]
+name = "unic-common"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
+
+[[package]]
+name = "unic-segment"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
+dependencies = [
+ "unic-ucd-segment",
+]
+
+[[package]]
+name = "unic-ucd-segment"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
+dependencies = [
+ "unic-char-property",
+ "unic-char-range",
+ "unic-ucd-version",
+]
+
+[[package]]
+name = "unic-ucd-version"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
+dependencies = [
+ "unic-common",
+]
+
+[[package]]
 name = "unicode-bidi"
 version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2263,6 +2610,17 @@
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi 0.3.9",
+ "winapi-util",
+]
+
+[[package]]
 name = "want"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2368,13 +2726,13 @@
 
 [[package]]
 name = "which"
-version = "4.2.5"
+version = "4.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
+checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
 dependencies = [
  "either",
- "lazy_static",
  "libc",
+ "once_cell",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index daf0fe2..dc2639c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+[workspace]
+members = [
+    ".",
+    "scripts",
+]
+
 [package]
 name = "skywalking-php"
 version = "0.1.0"
@@ -29,10 +35,6 @@
 name = "skywalking_agent"
 crate-type = ["lib", "cdylib"]
 
-[[bin]]
-name = "skywalking_agent"
-path = "src/main.rs"
-
 [dependencies]
 anyhow = "1.0.63"
 chrono = "0.4.22"
@@ -45,7 +47,7 @@
 once_cell = "1.13.1"
 phper = "0.5.0"
 prost = "0.11.0"
-skywalking = { version = "0.4.0", features = ["vendored"] }
+skywalking = "0.4.0"
 systemstat = "0.2.0"
 tokio = { version = "1.20.1", features = ["full"] }
 tokio-stream = "0.1.9"
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 7f9ace9..0000000
--- a/Makefile
+++ /dev/null
@@ -1,40 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-IS_DEBUG ?= 0
-CARGO_BIN ?= cargo
-CARGO_BUILD_FLAGS ?= 
-CARGO_TEST_FLAGS ?= 
-COMPOSER_BIN ?= composer
-
-cargo_flags != if [ $(IS_DEBUG) = 1 ]; then echo "" ; else echo "--release" ; fi
-target_dir != if [ $(IS_DEBUG) = 1 ]; then echo "debug" ; else echo "release" ; fi
-
-
-all: build
-
-build:
-	$(CARGO_BIN) build $(cargo_flags) $(CARGO_BUILD_FLAGS)
-
-test: build composer-install
-	$(CARGO_BIN) test $(cargo_flags) $(CARGO_TEST_FLAGS)
-
-install: build extension-install
-
-extension-install:
-	./target/$(target_dir)/skywalking_agent install
-
-composer-install:
-	$(COMPOSER_BIN) install --working-dir=tests/php
diff --git a/config.m4 b/config.m4
new file mode 100644
index 0000000..6b280ab
--- /dev/null
+++ b/config.m4
@@ -0,0 +1,69 @@
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+
+PHP_ARG_ENABLE([skywalking_agent],
+  [whether to enable skywalking_agent support],
+  [AS_HELP_STRING([--enable-skywalking_agent],
+    [Enable skywalking_agent support])],
+  [no])
+
+PHP_ARG_ENABLE([cargo_debug], [whether to enable cargo debug mode],
+[  --enable-cargo-debug           Enable cargo debug], no, no)
+
+if test "$PHP_THREAD_SAFETY" == "yes"; then
+  AC_MSG_ERROR([skywalking_agent does not support ZTS])
+fi
+
+if test "$PHP_SKYWALKING_AGENT" != "no"; then
+  AC_PATH_PROG(CARGO, cargo, no)
+  if ! test -x "$CARGO"; then
+    AC_MSG_ERROR([cargo command missing, please reinstall the cargo distribution])
+  fi
+
+  AC_PATH_PROG(PROTOC, protoc, no)
+  if ! test -x "$PROTOC"; then
+    AC_MSG_ERROR([protoc command missing, please reinstall the protoc distribution])
+  fi
+
+  AC_DEFINE(HAVE_SKYWALKING_AGENT, 1, [ Have skywalking_agent support ])
+
+  PHP_NEW_EXTENSION(skywalking_agent, [ ], $ext_shared)
+
+  CARGO_MODE_FLAGS="--release"
+  CARGO_MODE_DIR="release"
+
+  if test "$PHP_CARGO_DEBUG" != "no"; then
+    CARGO_MODE_FLAGS=""
+    CARGO_MODE_DIR="debug"
+  fi
+
+  echo -e "./modules/skywalking_agent.so:\n\tPHP_CONFIG=$PHP_PHP_CONFIG cargo build $CARGO_MODE_FLAGS\n\tcp ./target/$CARGO_MODE_DIR/libskywalking_agent.so ./modules/skywalking_agent.so" > Makefile.objects
+
+  PHP_MODULES="./modules/skywalking_agent.so"
+
+  AC_CONFIG_LINKS([ \
+    .rustfmt.toml:.rustfmt.toml \
+    Cargo.lock:Cargo.lock \
+    Cargo.toml:Cargo.toml \
+    LICENSE:LICENSE \
+    NOTICE:NOTICE \
+    README.md:README.md \
+    docker-compose.yml:docker-compose.yml \
+    docs:docs \
+    scripts:scripts \
+    src:src \
+    tests:tests \
+    ])
+fi
diff --git a/package.tpl.xml b/package.tpl.xml
new file mode 100644
index 0000000..c7f320d
--- /dev/null
+++ b/package.tpl.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<package version="2.0" 
+	xmlns="http://pear.php.net/dtd/package-2.0" 
+	xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" 
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+	<name>skywalking_agent</name>
+	<channel>pecl.php.net</channel>
+	<summary>Apache SkyWalking PHP Agent.</summary>
+	<description>The PHP Agent for Apache SkyWalking, which provides the native tracing abilities for PHP project.</description>
+	<lead>
+		<name>Apache SkyWalking</name>
+		<user>Apache SkyWalking</user>
+		<email>dev@skywalking.apache.org</email>
+		<active>yes</active>
+	</lead>
+	<!-- SkyWalking committers could be listed here as core maintainers. -->
+	<developer>
+		<name>jmjoy</name>
+		<user>jmjoy</user>
+		<email>jmjoy@apache.org</email>
+		<active>yes</active>
+	</developer>
+	<developer>
+		<name>Yanlong He</name>
+		<user>yanlong</user>
+		<email>yanlong@php.net</email>
+		<active>yes</active>
+	</developer>
+	<date>{{ date }}</date>
+	<version>
+		<release>{{ version }}</release>
+		<api>{{ version }}</api>
+	</version>
+	<stability>
+		<release>stable</release>
+		<api>stable</api>
+	</stability>
+	<license uri="http://www.apache.org/licenses/LICENSE-2.0.html">Apache-2.0</license>
+	<notes>
+		{{ notes }}
+	</notes>
+	<contents>
+		<dir name="/">
+			{% for file in files %}<file role="src" name="{{ file.path }}" />
+			{% endfor %}
+		</dir>
+	</contents>
+	<dependencies>
+		<required>
+			<php>
+				<min>7.1.0</min>
+			</php>
+			<pearinstaller>
+				<min>1.4.0</min>
+			</pearinstaller>
+			<os>
+				<name>windows</name>
+				<conflicts/>
+			</os>
+		</required>
+	</dependencies>
+	<providesextension>skywalking_agent</providesextension>
+	<extsrcrelease>
+		<configureoption default="no" name="enable-cargo-debug" prompt="enable cargo debug?" />
+	</extsrcrelease>
+</package>
diff --git a/scripts/Cargo.toml b/scripts/Cargo.toml
new file mode 100644
index 0000000..8a2e365
--- /dev/null
+++ b/scripts/Cargo.toml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[package]
+name = "scripts"
+version = "0.0.0"
+authors = ["Apache Software Foundation", "jmjoy <jmjoy@apache.org>", "Yanlong He <heyanlong@apache.org>"]
+description = "The Scripts of Apache SkyWalking PHP Agent."
+edition = "2021"
+rust-version = "1.58"
+repository = "https://github.com/apache/skywalking-php"
+license = "Apache-2.0"
+publish = false
+
+[dependencies]
+anyhow = "1.0.64"
+chrono = "0.4.22"
+clap = { version = "3.2.20", features = ["derive"] }
+serde = { version = "1.0.144", features = ["derive"] }
+tera = "1.17.0"
+tracing = "0.1.36"
+tracing-subscriber = "0.3.15"
diff --git a/scripts/src/command/create_package_xml.rs b/scripts/src/command/create_package_xml.rs
new file mode 100644
index 0000000..8c12958
--- /dev/null
+++ b/scripts/src/command/create_package_xml.rs
@@ -0,0 +1,96 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use chrono::{DateTime, Local};
+use clap::Parser;
+use serde::Serialize;
+use std::{fs, path::PathBuf, process::Command, time::SystemTime};
+use tera::{Context, Tera};
+use tracing::info;
+
+/// Create package.xml from template file.
+#[derive(Parser, Debug)]
+pub struct CreatePackageXmlCommand {
+    /// Template file path.
+    #[clap(long, default_value = "./package.tpl.xml")]
+    tpl_path: PathBuf,
+
+    /// Target file path.
+    #[clap(long, default_value = "./package.xml")]
+    target_path: PathBuf,
+
+    /// Project directory path.
+    #[clap(long, default_value = ".")]
+    project_path: PathBuf,
+
+    /// Version of skywalking_agent.
+    #[clap(long)]
+    version: String,
+
+    /// Release date, default is current local timezone date.
+    #[clap(long)]
+    date: Option<String>,
+
+    /// Release notes.
+    #[clap(long)]
+    notes: String,
+}
+
+#[derive(Serialize)]
+struct File {
+    path: String,
+}
+
+impl CreatePackageXmlCommand {
+    pub fn run(&self) -> anyhow::Result<()> {
+        info!(tpl_path = ?&self.tpl_path, "read template content");
+        let tpl = fs::read_to_string(&self.tpl_path)?;
+
+        let mut context = Context::new();
+        context.insert("date", &self.get_date());
+        context.insert("version", &self.version);
+        context.insert("notes", &self.notes);
+        context.insert("files", &self.get_git_files()?);
+
+        let contents = Tera::one_off(&tpl, &context, false)?;
+        info!(target_path = ?&self.target_path, "write target content");
+        fs::write(&self.target_path, contents)?;
+
+        Ok(())
+    }
+
+    fn get_date(&self) -> String {
+        match &self.date {
+            Some(date) => date.to_owned(),
+            None => {
+                let datetime: DateTime<Local> = SystemTime::now().into();
+                datetime.format("%Y-%m-%d").to_string()
+            }
+        }
+    }
+
+    fn get_git_files(&self) -> anyhow::Result<Vec<File>> {
+        let output = Command::new("git")
+            .args(["ls-tree", "-r", "HEAD", "--name-only"])
+            .output()?;
+        let content = String::from_utf8(output.stdout)?;
+        Ok(content
+            .split_whitespace()
+            .map(|path| File {
+                path: path.to_owned(),
+            })
+            .collect())
+    }
+}
diff --git a/src/main.rs b/scripts/src/command/mod.rs
similarity index 68%
rename from src/main.rs
rename to scripts/src/command/mod.rs
index 2520f18..ab291be 100644
--- a/src/main.rs
+++ b/scripts/src/command/mod.rs
@@ -13,8 +13,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use phper::cmd::make;
+mod create_package_xml;
 
-fn main() {
-    make();
+use self::create_package_xml::CreatePackageXmlCommand;
+use clap::Subcommand;
+
+#[derive(Subcommand, Debug)]
+pub enum Commands {
+    CreatePackageXml(CreatePackageXmlCommand),
+}
+
+impl Commands {
+    pub fn run(&self) -> anyhow::Result<()> {
+        match self {
+            Commands::CreatePackageXml(cmd) => cmd.run(),
+        }
+    }
 }
diff --git a/scripts/src/main.rs b/scripts/src/main.rs
new file mode 100644
index 0000000..0d814bd
--- /dev/null
+++ b/scripts/src/main.rs
@@ -0,0 +1,49 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod command;
+
+use crate::command::Commands;
+use anyhow::Context;
+use clap::Parser;
+use tracing::metadata::LevelFilter;
+use tracing_subscriber::FmtSubscriber;
+
+/// Args.
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct Args {
+    /// Log level.
+    #[clap(long, default_value = "INFO")]
+    log_level: LevelFilter,
+
+    #[clap(subcommand)]
+    command: Commands,
+}
+
+fn setup_log(args: &Args) -> anyhow::Result<()> {
+    let subscriber = FmtSubscriber::builder()
+        .with_max_level(args.log_level)
+        .finish();
+
+    tracing::subscriber::set_global_default(subscriber).context("setting default subscriber failed")
+}
+
+fn main() -> anyhow::Result<()> {
+    let args = Args::parse();
+    setup_log(&args)?;
+    args.command.run()?;
+    Ok(())
+}