brpc_build/
lib.rs

1// Copyright 2019 Baidu, Inc.
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// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//! `brpc-build` compiles `.proto` files for `brpc-rs`.
15//!
16//! `brpc-build` is designed to be used for build-time code generation as part of
17//! a Cargo build-script.
18
19use std::{env, io, path, process};
20
21/// Compile .proto files into Rust files during a Cargo build.
22///
23/// The generated `.rs` files will be written to the Cargo `OUT_DIR` directory,
24/// suitable for use with the `include!` macro.
25///
26/// This function should be called in a project's `build.rs`.
27///
28/// # Arguments
29///
30/// **`protos`** - Paths to `.proto` files to compile. Any transitively
31/// [imported][3] `.proto` files will automatically be included.
32///
33/// **`includes`** - Paths to directories in which to search for imports.
34/// Directories will be searched in order. The `.proto` files passed in
35/// **`protos`** must be found in one of the provided include directories.
36///
37/// It's expected that this function call be `unwrap`ed in a `build.rs`; there
38/// is typically no reason to gracefully recover from errors during a build.
39///
40/// # Example `build.rs`
41///
42/// ```norun
43/// fn main() {
44///     brpc_build::compile_protos(&["src/echo.proto",],
45///                                 &["src"]).unwrap();
46/// }
47/// ```
48pub fn compile_protos<P>(protos: &[P], includes: &[P]) -> io::Result<()>
49where
50    P: AsRef<path::Path>,
51{
52    let out_dir_path: path::PathBuf = env::var_os("OUT_DIR")
53        .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "OUT_DIR env var is not set"))
54        .map(Into::into)?;
55    let out_dir = out_dir_path.as_os_str();
56
57    // Step 0
58    let _ = prost_build::compile_protos(protos, includes)?;
59
60    // Step 1
61    let mut cmd = process::Command::new("protoc");
62    for include in includes {
63        cmd.arg("-I").arg(include.as_ref());
64    }
65    for proto in protos {
66        cmd.arg(proto.as_ref());
67    }
68    cmd.arg("--plugin=protoc-gen=brpc=`which protoc-gen-brpc`");
69    cmd.arg("--brpc_out").arg(&out_dir);
70
71    let output = cmd.output()?;
72    if !output.status.success() {
73        return Err(io::Error::new(
74            io::ErrorKind::Other,
75            format!(
76                "protoc failed in the first pass: {}",
77                String::from_utf8_lossy(&output.stderr)
78            ),
79        ));
80    }
81
82    // Step 2
83    let mut cmd = process::Command::new("protoc");
84    let current_dir = out_dir_path.to_path_buf();
85    cmd.arg("-I").arg(out_dir_path.to_path_buf());
86    for proto in protos {
87        let f = proto
88            .as_ref()
89            .file_name()
90            .ok_or(io::Error::new(io::ErrorKind::Other, "Invalid file name"))?;
91        cmd.arg(current_dir.join(f));
92    }
93    cmd.arg("--cpp_out").arg(out_dir_path.to_path_buf());
94    let output = cmd.output()?;
95    if !output.status.success() {
96        return Err(io::Error::new(
97            io::ErrorKind::Other,
98            format!(
99                "protoc failed in the second pass: {}",
100                String::from_utf8_lossy(&output.stderr)
101            ),
102        ));
103    }
104
105    // Step 3
106    let mut builder = cc::Build::new();
107    for proto in protos {
108        let mut cc_to_build = out_dir_path.to_path_buf();
109        let f = proto
110            .as_ref()
111            .file_name()
112            .ok_or(io::Error::new(io::ErrorKind::Other, "Invalid file name"))?;
113        cc_to_build.push(f);
114        cc_to_build.set_extension("brpc.cc");
115        builder.file(&cc_to_build);
116
117        let mut cc_to_build = out_dir_path.to_path_buf();
118        let f = proto
119            .as_ref()
120            .file_name()
121            .ok_or(io::Error::new(io::ErrorKind::Other, "Invalid file name"))?;
122        cc_to_build.push(f);
123        cc_to_build.set_extension("pb.cc");
124        builder.file(&cc_to_build);
125    }
126
127    builder.cpp(true).flag("-std=c++11").warnings(false);
128    builder.compile("brpc_service");
129    println!("cargo:rustc-link-lib=static=brpc_service");
130
131    println!("cargo:rustc-link-lib=brpc");
132    println!("cargo:rustc-link-lib=protobuf");
133    println!("cargo:rustc-link-lib=gflags");
134    println!("cargo:rustc-link-lib=leveldb");
135    println!("cargo:rustc-link-lib=ssl");
136    println!("cargo:rustc-link-lib=crypto");
137
138    Ok(())
139}