Skip to content

Commit 7bb9621

Browse files
authored
feat: allow using pre-generated .rs files for protocol buffers. (#553)
When YARA-X is build, the .proto files that describe modules are automatically converted into .rs files. This relies on using the protobuf_codegen create or the protoc compiler. However, in certain cases is useful to have pre-generated .rs files that can be used during compilation, instead of having to convert the .proto files into .rs files all the time. This PR introduces such feature. The default behavior is still generating the .rs files with every build, but now you can use pre-generated files in contained in the protos/generated folder, by disabling the generate-proto-code feature (which is enabled by default).
1 parent 8141513 commit 7bb9621

34 files changed

+56154
-36
lines changed

.github/workflows/release.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,11 @@ jobs:
230230

231231
- name: Publish crate
232232
env:
233-
# Don't re-generate the files `modules.rs` and `add_modules.rs`, use
234-
# the ones in the repository. These files can't be regenerated because
235-
# `cargo publish` checks that the repository doesn't change during
236-
# the build. See: https://github.com/rust-lang/cargo/pull/5584
233+
# Don't re-generate `modules.rs`, `add_modules.rs` and the files in
234+
# `protos/generated`. Use the ones in the repository. These files can't
235+
# be regenerated because `cargo publish` checks that the repository
236+
# doesn't change during the build.
237+
# See: https://github.com/rust-lang/cargo/pull/5584
237238
YRX_REGENERATE_MODULES_RS: "no"
238239
run: |
239240
cargo login ${{ secrets.CRATES_IO_API_TOKEN }}

lib/Cargo.toml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,23 @@ exact-atoms = []
4949
# the fast regexp matching mechanism for testing purposes.
5050
fast-regexp = []
5151

52+
# Enables the generation of Rust code from .proto files. This feature is enabled
53+
# by default. When disabled, the build process will use pre-generated Rust files
54+
# found in the source tree, instead of generating them at build time.
55+
generate-proto-code = ["dep:protobuf-codegen", "dep:protobuf-parse"]
56+
5257
# Uses the `inventory` crate (https://github.com/dtolnay/inventory) instead
53-
# of `linkme` (https://github.com/dtolnay/linkme) for tracking WASM exports.
58+
# of `linkme` (https://github.com/dtolnay/linkme) for tracking WASM exports.
5459
#
5560
# This feature is disabled by default.
5661
inventory = ["dep:inventory"]
5762

5863
# Whether to use protoc for parsing and compiling .proto files. By default,
5964
# .proto files are parsed and compiled by the pure-Rust compiler implemented
60-
# by the `rust-protobuf` crate. With this feature you can change this behavior
61-
# and use protoc, the official Protocol Buffer compiler. You'll need to have
62-
# protoc installed in your system, together with the protoc-gen-rust plugin.
63-
# Follow the instructions in: https://lib.rs/crates/protobuf-codegen3
65+
# by the `protobuf_codegen` crate. With this feature you can change this
66+
# behavio and use `protoc`, the official Protocol Buffer compiler. You'll need
67+
# to have protoc installed in your system, together with the protoc-gen-rust
68+
# plugin. Follow the instructions in: https://lib.rs/crates/protobuf-codegen3
6469
protoc = []
6570

6671
# Uses the `linkme` crate (https://github.com/dtolnay/linkme) instead
@@ -246,6 +251,7 @@ default = [
246251
"constant-folding",
247252
"default-modules",
248253
"fast-regexp",
254+
"generate-proto-code",
249255
"linkme",
250256
]
251257

@@ -317,8 +323,8 @@ lingua = { version = "1.6.2", optional = true, default-features = false, feature
317323
anyhow = { workspace = true }
318324
globwalk = { workspace = true }
319325
protobuf = { workspace = true }
320-
protobuf-codegen = { workspace = true }
321-
protobuf-parse = { workspace = true }
326+
protobuf-codegen = { workspace = true, optional = true }
327+
protobuf-parse = { workspace = true, optional = true }
322328
yara-x-proto = { workspace = true }
323329

324330
[dev-dependencies]

lib/build.rs

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
use anyhow::Context;
1+
#[cfg(feature = "generate-proto-code")]
22
use protobuf::descriptor::FileDescriptorProto;
3-
use std::fs::File;
4-
use std::io::Write;
5-
use std::path::PathBuf;
6-
use std::{env, fs};
73

8-
use protobuf_codegen::Codegen;
9-
use protobuf_parse::Parser;
4+
#[cfg(feature = "generate-proto-code")]
5+
fn generate_module_files(proto_files: Vec<FileDescriptorProto>) {
6+
use std::fs::File;
7+
use std::io::Write;
8+
use std::path::PathBuf;
9+
use yara_x_proto::exts::module_options as yara_module_options;
1010

11-
use yara_x_proto::exts::module_options as yara_module_options;
11+
println!("cargo:rerun-if-changed=src/modules/add_modules.rs");
12+
println!("cargo:rerun-if-changed=src/modules/modules.rs");
1213

13-
fn generate_module_files(proto_files: Vec<FileDescriptorProto>) {
1414
let mut modules = Vec::new();
1515
// Look for .proto files that describe a YARA module. A proto that
1616
// describes a YARA module has yara.module_options, like...
@@ -141,16 +141,14 @@ add_module!(modules, "{name}", {proto_mod}, "{root_message}", {rust_mod_name}, {
141141
write!(add_modules_rs, "\n}}").unwrap();
142142
}
143143

144-
fn main() {
145-
println!("cargo:rerun-if-changed=src/modules/protos");
146-
println!("cargo:rerun-if-changed=src/modules/add_modules.rs");
147-
println!("cargo:rerun-if-changed=src/modules/modules.rs");
144+
#[cfg(feature = "generate-proto-code")]
145+
fn generate_proto_code() {
146+
use anyhow::Context;
147+
use std::path::PathBuf;
148+
use std::{env, fs};
148149

149-
if !cfg!(feature = "inventory") && !cfg!(feature = "linkme") {
150-
panic!(
151-
"either the `inventory` feature or the `linkme` feature must be enabled."
152-
);
153-
}
150+
use protobuf_codegen::Codegen;
151+
use protobuf_parse::Parser;
154152

155153
let mut proto_compiler = Codegen::new();
156154
let mut proto_parser = Parser::new();
@@ -163,6 +161,8 @@ fn main() {
163161
proto_parser.pure();
164162
}
165163

164+
println!("cargo:rerun-if-changed=src/modules/protos");
165+
166166
proto_compiler.cargo_out_dir("protos").include("./src/modules/protos");
167167
proto_parser.include("./src/modules/protos");
168168

@@ -235,28 +235,53 @@ fn main() {
235235
// Generate .rs files for .proto files in src/modules/protos
236236
proto_compiler.run_from_script();
237237

238-
// Decide whether the `modules.rs` and `add_modules.rs` files should be
239-
// re-generated. By default, they will be re-generated.
240-
let mut regenerate_modules_rs = true;
238+
// Decide whether `modules.rs`, `add_modules.rs` and the content of the
239+
// `protos/generated` directory should be re-generated. By default, they
240+
// will be re-generated.
241+
let mut regenerate = true;
241242

242243
// If the environment variable `YRX_REGENERATE_MODULES_RS` is present, the
243244
// files won't be re-generated if the value is "false", "no" or "0". Any
244245
// other value will re-generate the files.
245246
if let Ok(env_var) = env::var("YRX_REGENERATE_MODULES_RS") {
246-
regenerate_modules_rs =
247-
env_var != "false" && env_var != "no" && env_var != "0";
247+
regenerate = env_var != "false" && env_var != "no" && env_var != "0";
248248
}
249249

250250
// Also, don't re-generate the files if `DOCS_RS` is defined. This is
251251
// because doc.rs puts the source code in a read-only file system, and
252252
// we can't modify the files.
253253
if env::var("DOCS_RS").is_ok() {
254-
regenerate_modules_rs = false;
254+
regenerate = false;
255255
}
256256

257-
if regenerate_modules_rs {
257+
if regenerate {
258258
generate_module_files(
259259
proto_parser.file_descriptor_set().unwrap().file,
260260
);
261+
262+
let out_dir = env::var("OUT_DIR").unwrap();
263+
let src_dir = PathBuf::from("src/modules/protos/generated");
264+
let _ = fs::create_dir_all(&src_dir);
265+
266+
for entry in globwalk::glob(format!("{}/protos/*.rs", out_dir))
267+
.unwrap()
268+
.flatten()
269+
{
270+
let path = entry.path();
271+
let file_name = path.file_name().unwrap();
272+
let dest = src_dir.join(file_name);
273+
fs::copy(path, dest).unwrap();
274+
}
275+
}
276+
}
277+
278+
fn main() {
279+
if !cfg!(feature = "inventory") && !cfg!(feature = "linkme") {
280+
panic!(
281+
"either the `inventory` feature or the `linkme` feature must be enabled."
282+
);
261283
}
284+
285+
#[cfg(feature = "generate-proto-code")]
286+
generate_proto_code();
262287
}

lib/src/modules/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ use rustc_hash::FxHashMap;
77
use thiserror::Error;
88

99
pub mod protos {
10+
#[cfg(feature = "generate-proto-code")]
1011
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
12+
13+
#[cfg(not(feature = "generate-proto-code"))]
14+
include!("protos/generated/mod.rs");
1115
}
1216

1317
#[cfg(test)]

0 commit comments

Comments
 (0)