esp_idf_build/
lib.rs

1use std::{env, io, path, process, sync::mpsc, thread, time};
2
3/// Runs make/cmake to ensure native components are built.
4/// Even once built this will add a few seconds to your build time.
5pub fn build_native() -> thread::Result<()> {
6    let start = time::Instant::now();
7    //let spinner = indicatif::ProgressBar::new_spinner();
8    let (tx, rx) = mpsc::sync_channel(1);
9    let make = thread::spawn(move || {
10        let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
11        if cfg!(feature = "build_idf") {
12            let res = process::Command::new("idf.py")
13                .arg("build")
14                .current_dir(&path::Path::new(&manifest_dir))
15                .status();
16            match res {
17                Ok(_status) => {},
18                // If `idf.py` is not in $PATH:
19                //
20                // thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', /home/matrix-io/esp_idf_build/src/lib.rs:12:13
21                // note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
22                // thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Any', build.rs:4:5
23                Err(error) if error.kind() == io::ErrorKind::NotFound => panic!("Not found. `source $IDF_PATH/export.sh`?"),
24                Err(error) => panic!("{}", error),
25            }
26        } else if cfg!(feature = "build_make") {
27            process::Command::new("make")
28                .current_dir(&path::Path::new(&manifest_dir))
29                .status()
30                .unwrap();
31        } else {
32            println!("cargo:warning=No build feature enabled.  Build skipped.");
33        }
34
35        tx.send(()).unwrap();
36    });
37    loop {
38        match rx.try_recv() {
39            Err(mpsc::TryRecvError::Disconnected) | Ok(_) => break,
40            Err(mpsc::TryRecvError::Empty) => {}
41        }
42        // NOTE: no point in printing anything because it's buffered and no way to flush it.
43        //println!("cargo:warning=Building...");
44        //spinner.tick();
45        thread::sleep(time::Duration::from_millis(1000));
46    }
47    let ret = make.join();
48    //spinner.finish();
49    println!("cargo:warning=ESP-IDF build_native() time {:?}", start.elapsed());
50    ret
51}
52
53/// Add ESP-IDF native library search paths to rustc.
54pub fn print_link_search() {
55    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
56    if target_arch == "xtensa" {
57        let esp_idf =
58            path::PathBuf::from(env::var("IDF_PATH").expect("IDF_PATH environment variable must be set"));
59        // Folder containing `build/` output after running `make menuconfig && make`
60        let build_subdir = path::PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("build");
61        if glob::glob(&build_subdir.join("*.bin").to_string_lossy())
62            .unwrap()
63            .next()
64            .is_none()
65        {
66            panic!("No .bin files, did you run `make menuconfig && make`?");
67        }
68
69        let build_dirs = [
70            // Directories when using `idf.py build`
71            "esp-idf/esp32",
72            "esp-idf/esp32/ld",
73
74            "app_trace",
75            "app_update",
76            "asio",
77            "bootloader_support",
78            "bt",
79            "cbor",
80            "coap",
81            "console",
82            "cxx",
83            "driver",
84            "efuse",
85            "esp-tls",
86            "esp32",
87            "esp_adc_cal",
88            "esp_common",
89            "esp_eth",
90            "esp_event",
91            "esp_gdbstub",
92            "esp_hid",
93            "esp_http_client",
94            "esp_http_server",
95            "esp_https_ota",
96            "esp_https_server",
97            "esp_ipc",
98            "esp_local_ctrl",
99            "esp_netif",
100            "esp_ringbuf",
101            "esp_rom",
102            "esp_serial_slave_link",
103            "esp_system",
104            "esp_timer",
105            "esp_websocket_client",
106            "esp_wifi",
107            "espcoredump",
108            "ethernet",
109            "expat",
110            "fatfs",
111            "freemodbus",
112            "freertos",
113            "heap",
114            "idf_test",
115            "jsmn",
116            "json",
117            "libsodium",
118            "log",
119            "lwip",
120            "main",
121            "mbedtls",
122            "mdns",
123            "mqtt",
124            "newlib",
125            "nghttp",
126            "nvs_flash",
127            "openssl",
128            "perfmon",
129            "protobuf-c",
130            "protocomm",
131            "pthread",
132            "sdmmc",
133            "smartconfig_ack",
134            "soc",
135            "spi_flash",
136            "spiffs",
137            "tcp_transport",
138            "tcpip_adapter",
139            "ulp",
140            "unity",
141            "vfs",
142            "wear_levelling",
143            "wifi_provisioning",
144            "wpa_supplicant",
145            "xtensa",
146        ]
147        .iter()
148        .map(|subdir| build_subdir.join(subdir));
149        let idf_components = [
150            "esp32/ld",
151            "esp_rom/esp32/ld",
152            "esp_wifi/lib/esp32",
153            "xtensa/esp32",
154        ]
155        .iter()
156        .map(|subdir| esp_idf.join("components").join(subdir));
157
158        for dir in build_dirs.chain(idf_components) {
159            println!("cargo:rustc-link-search=native={}", dir.display());
160        }
161    }
162}
163
164/// Writes script file that uses esptool.py to create an application image from
165/// the Rust ELF.
166pub fn esptool_write_script() -> io::Result<()> {
167    use io::Write;
168    let root = path::PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
169    let mut file = std::fs::File::create(root.join("image.sh"))?;
170
171    // Make it executable
172    use std::os::unix::fs::PermissionsExt;
173    let perms = std::fs::Permissions::from_mode(0o744);
174    file.set_permissions(perms)?;
175    let cmd = format!(
176        r#""$IDF_PATH/components/esptool_py/esptool/esptool.py" \
177    --chip esp32 \
178    elf2image \
179    -o build/esp-app.bin \
180    target/{}/{}/{}"#,
181        env::var("TARGET").unwrap(),
182        env::var("PROFILE").unwrap(),
183        env::var("CARGO_PKG_NAME").unwrap()
184    );
185
186    // Write script with she-bang (#!)
187    write!(file, "#!/usr/bin/env bash\n\n{}", cmd)
188}