Skip to content

Commit 7ccfe35

Browse files
author
RedZapdos123
committed
fix: allow plotly_static to compile without drivers
Allow plotly_static to compile without driver features so fresh workspace builds and no-feature plotly test builds no longer fail. Keep chromedriver/geckodriver mutually exclusive for actual export use, return a runtime error when export is attempted without a driver feature, and suppress the no-feature build-script unused warnings that trigger a rustc ICE on stable. Signed-off-by: Mridankan Mandal <xerontitan90@gmail.com>
1 parent 6011077 commit 7ccfe35

6 files changed

Lines changed: 84 additions & 63 deletions

File tree

plotly_static/Cargo.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ keywords = ["plotly", "static", "image", "export", "webdriver"]
1212

1313
exclude = ["target/*"]
1414

15-
[features]
16-
default = ["chromedriver"]
17-
webdriver_download = []
18-
geckodriver = []
19-
chromedriver = []
15+
[features]
16+
webdriver_download = []
17+
geckodriver = []
18+
chromedriver = []
2019
# This is used for enabling extra debugging messages and debugging functionality
2120
debug = []
2221

plotly_static/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ serde_json = "1.0"
6363

6464
### Feature Flags
6565

66+
- To use static export, enable exactly one of the driver features below.
6667
- `chromedriver`: Use Chromedriver and Chrome/Chromium browser for rendering and export
6768
- `geckodriver`: Use Geckodriver Firefox browser for rendering for rendering and export
6869
- `webdriver_download`: Auto-download the chosen WebDriver binary

plotly_static/build.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
#![cfg_attr(
2+
not(any(feature = "geckodriver", feature = "chromedriver")),
3+
allow(unused)
4+
)]
5+
16
use std::env;
27
use std::fs;
38
use std::path::{Path, PathBuf};
@@ -8,9 +13,9 @@ use anyhow::{anyhow, Context, Result};
813
use tokio::time::sleep;
914
use webdriver_downloader::prelude::*;
1015

11-
// Enforce that at least one driver feature is enabled
12-
#[cfg(not(any(feature = "geckodriver", feature = "chromedriver")))]
13-
compile_error!("At least one of 'geckodriver' or 'chromedriver' features must be enabled.");
16+
// Enforce that only one driver feature is enabled
17+
#[cfg(all(feature = "geckodriver", feature = "chromedriver"))]
18+
compile_error!("Only one of 'geckodriver' or 'chromedriver' features can be enabled at a time.");
1419

1520
#[cfg(target_os = "windows")]
1621
const DRIVER_EXT: &str = ".exe";
@@ -288,9 +293,6 @@ async fn download(
288293
}
289294

290295
fn main() -> Result<()> {
291-
#[cfg(all(feature = "geckodriver", feature = "chromedriver"))]
292-
println!("cargo::warning=Both 'geckodriver' and 'chromedriver' features are enabled. 'geckodriver' will take priority.");
293-
294296
if cfg!(feature = "webdriver_download") {
295297
println!("cargo:rerun-if-changed=src/lib.rs");
296298
let webdriver_bin_dir = user_bin_dir();
@@ -299,7 +301,7 @@ fn main() -> Result<()> {
299301
webdriver_bin_dir.to_string_lossy()
300302
);
301303

302-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
304+
#[cfg(feature = "chromedriver")]
303305
{
304306
let config = WebdriverDownloadConfig {
305307
driver_name: CHROMEDRIVER_NAME,
@@ -322,7 +324,7 @@ fn main() -> Result<()> {
322324
println!("cargo::warning=No specific driver feature enabled, skipping driver setup");
323325
}
324326
} else {
325-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
327+
#[cfg(feature = "chromedriver")]
326328
{
327329
let msg = format!("'webdriver_download' feature disabled. Please install a '{CHROMEDRIVER_NAME}' version manually and make the environment variable 'WEBDRIVER_PATH' point to it.");
328330
println!("cargo::warning={msg}");

plotly_static/src/lib.rs

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@
5959
//!
6060
//! ## Features and Dependencies
6161
//!
62-
//! ### Required Features
62+
//! ### Driver Features
6363
//!
64-
//! You must enable one of the following features:
64+
//! To use static export, enable one of the following features:
6565
//!
6666
//! - `chromedriver`: Use Chrome/Chromium for rendering
6767
//! - `geckodriver`: Use Firefox for rendering
@@ -294,6 +294,7 @@ use fantoccini::{wd::Capabilities, Client, ClientBuilder};
294294
#[cfg(not(any(test, feature = "debug")))]
295295
use log::{debug, error, warn};
296296
use serde::Serialize;
297+
#[cfg(any(feature = "chromedriver", feature = "geckodriver"))]
297298
use serde_json::map::Map as JsonMap;
298299
use urlencoding::encode;
299300
use webdriver::WebDriver;
@@ -461,20 +462,24 @@ impl Default for StaticExporterBuilder {
461462
offline_mode: false,
462463
pdf_export_timeout: 150,
463464
webdriver_browser_caps: {
464-
#[cfg(feature = "geckodriver")]
465+
#[cfg(feature = "chromedriver")]
465466
{
466-
crate::webdriver::firefox_default_caps()
467+
crate::webdriver::chrome_default_caps()
467468
.into_iter()
468469
.map(|s| s.to_string())
469470
.collect()
470471
}
471-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
472+
#[cfg(feature = "geckodriver")]
472473
{
473-
crate::webdriver::chrome_default_caps()
474+
crate::webdriver::firefox_default_caps()
474475
.into_iter()
475476
.map(|s| s.to_string())
476477
.collect()
477478
}
479+
#[cfg(not(any(feature = "chromedriver", feature = "geckodriver")))]
480+
{
481+
Vec::new()
482+
}
478483
},
479484
}
480485
}
@@ -923,6 +928,10 @@ pub struct AsyncStaticExporter {
923928
pdf_export_timeout: u32,
924929

925930
/// Browser command-line flags (e.g., "--headless", "--no-sandbox")
931+
#[cfg_attr(
932+
not(any(feature = "chromedriver", feature = "geckodriver")),
933+
allow(dead_code)
934+
)]
926935
webdriver_browser_caps: Vec<String>,
927936

928937
/// Cached WebDriver client for session reuse
@@ -1117,46 +1126,55 @@ impl AsyncStaticExporter {
11171126
}
11181127

11191128
fn build_webdriver_caps(&self) -> Result<Capabilities> {
1120-
// Define browser capabilities (copied to avoid reordering existing code)
1121-
let mut caps = JsonMap::new();
1122-
let mut browser_opts = JsonMap::new();
1123-
let browser_args = self.webdriver_browser_caps.clone();
1124-
1125-
browser_opts.insert("args".to_string(), serde_json::json!(browser_args));
1126-
1127-
// Add Chrome binary capability if BROWSER_PATH is set
1128-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
1129-
if let Ok(chrome_path) = std::env::var("BROWSER_PATH") {
1130-
browser_opts.insert("binary".to_string(), serde_json::json!(chrome_path));
1131-
debug!("Added Chrome binary capability: {chrome_path}");
1132-
}
1133-
// Add Firefox binary capability if BROWSER_PATH is set
1134-
#[cfg(feature = "geckodriver")]
1135-
if let Ok(firefox_path) = std::env::var("BROWSER_PATH") {
1136-
browser_opts.insert("binary".to_string(), serde_json::json!(firefox_path));
1137-
debug!("Added Firefox binary capability: {firefox_path}");
1138-
}
1139-
1140-
// Add Firefox-specific preferences for CI environments
1141-
#[cfg(feature = "geckodriver")]
1129+
#[cfg(not(any(feature = "chromedriver", feature = "geckodriver")))]
11421130
{
1143-
let prefs = common::get_firefox_ci_preferences();
1144-
browser_opts.insert("prefs".to_string(), serde_json::json!(prefs));
1145-
debug!("Added Firefox preferences for CI compatibility");
1131+
Err(anyhow!(
1132+
"Static image export requires enabling either the 'chromedriver' or 'geckodriver' feature."
1133+
))
11461134
}
1135+
#[cfg(any(feature = "chromedriver", feature = "geckodriver"))]
1136+
{
1137+
// Define browser capabilities (copied to avoid reordering existing code)
1138+
let mut caps = JsonMap::new();
1139+
let mut browser_opts = JsonMap::new();
1140+
let browser_args = self.webdriver_browser_caps.clone();
1141+
1142+
browser_opts.insert("args".to_string(), serde_json::json!(browser_args));
1143+
1144+
// Add Chrome binary capability if BROWSER_PATH is set
1145+
#[cfg(feature = "chromedriver")]
1146+
if let Ok(chrome_path) = std::env::var("BROWSER_PATH") {
1147+
browser_opts.insert("binary".to_string(), serde_json::json!(chrome_path));
1148+
debug!("Added Chrome binary capability: {chrome_path}");
1149+
}
1150+
// Add Firefox binary capability if BROWSER_PATH is set
1151+
#[cfg(feature = "geckodriver")]
1152+
if let Ok(firefox_path) = std::env::var("BROWSER_PATH") {
1153+
browser_opts.insert("binary".to_string(), serde_json::json!(firefox_path));
1154+
debug!("Added Firefox binary capability: {firefox_path}");
1155+
}
11471156

1148-
caps.insert(
1149-
"browserName".to_string(),
1150-
serde_json::json!(get_browser_name()),
1151-
);
1152-
caps.insert(
1153-
get_options_key().to_string(),
1154-
serde_json::json!(browser_opts),
1155-
);
1157+
// Add Firefox-specific preferences for CI environments
1158+
#[cfg(feature = "geckodriver")]
1159+
{
1160+
let prefs = common::get_firefox_ci_preferences();
1161+
browser_opts.insert("prefs".to_string(), serde_json::json!(prefs));
1162+
debug!("Added Firefox preferences for CI compatibility");
1163+
}
1164+
1165+
caps.insert(
1166+
"browserName".to_string(),
1167+
serde_json::json!(get_browser_name()),
1168+
);
1169+
caps.insert(
1170+
get_options_key().to_string(),
1171+
serde_json::json!(browser_opts),
1172+
);
11561173

1157-
debug!("WebDriver capabilities: {caps:?}");
1174+
debug!("WebDriver capabilities: {caps:?}");
11581175

1159-
Ok(caps)
1176+
Ok(caps)
1177+
}
11601178
}
11611179

11621180
#[cfg(target_os = "windows")]
@@ -1606,7 +1624,7 @@ mod tests {
16061624
}
16071625

16081626
#[test]
1609-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
1627+
#[cfg(feature = "chromedriver")]
16101628
// Skip this test for geckodriver as it doesn't support multiple concurrent
16111629
// sessions on the same process as gracefully as chromedriver
16121630
fn test_webdriver_process_reuse() {
@@ -1671,7 +1689,7 @@ mod tests {
16711689
}
16721690
}
16731691

1674-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
1692+
#[cfg(feature = "chromedriver")]
16751693
mod chrome {
16761694
/// Returns the browser name for Chrome WebDriver.
16771695
///
@@ -1709,8 +1727,7 @@ mod firefox {
17091727
}
17101728
}
17111729

1712-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
1730+
#[cfg(feature = "chromedriver")]
17131731
use chrome::{get_browser_name, get_options_key};
17141732
#[cfg(feature = "geckodriver")]
17151733
use firefox::{get_browser_name, get_options_key};
1716-

plotly_static/src/template.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub(crate) fn image_export_js_script() -> String {
3939
}
4040

4141
pub(crate) fn pdf_export_js_script(timeout_ms: u32) -> String {
42-
let foreign_object_rendering = if cfg!(all(feature = "chromedriver", not(feature = "geckodriver"))) {
42+
let foreign_object_rendering = if cfg!(feature = "chromedriver") {
4343
"true"
4444
} else {
4545
"false"

plotly_static/src/webdriver.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@ const WEBDRIVER_PATH_ENV: &str = "WEBDRIVER_PATH";
2626
#[cfg(feature = "geckodriver")]
2727
const WEBDRIVER_BIN: &str = "geckodriver";
2828

29-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver")))]
29+
#[cfg(feature = "chromedriver")]
3030
const WEBDRIVER_BIN: &str = "chromedriver";
3131

32+
#[cfg(not(any(feature = "chromedriver", feature = "geckodriver")))]
33+
const WEBDRIVER_BIN: &str = "webdriver";
3234

3335
/// Default WebDriver port
3436
pub(crate) const WEBDRIVER_PORT: u32 = 4444;
3537
/// Default WebDriver URL
3638
pub(crate) const WEBDRIVER_URL: &str = "http://127.0.0.1";
3739

38-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver"), not(target_os = "windows")))]
40+
#[cfg(all(feature = "chromedriver", not(target_os = "windows")))]
3941
pub(crate) fn chrome_default_caps() -> Vec<&'static str> {
4042
vec![
4143
"--headless",
@@ -66,7 +68,7 @@ pub(crate) fn chrome_default_caps() -> Vec<&'static str> {
6668
]
6769
}
6870

69-
#[cfg(all(feature = "chromedriver", not(feature = "geckodriver"), target_os = "windows"))]
71+
#[cfg(all(feature = "chromedriver", target_os = "windows"))]
7072
pub(crate) fn chrome_default_caps() -> Vec<&'static str> {
7173
vec![
7274
"--headless=new",
@@ -556,7 +558,7 @@ impl WebDriver {
556558
}
557559

558560
// Check if chromedriver is in PATH (Windows-specific)
559-
#[cfg(all(target_os = "windows", feature = "chromedriver", not(feature = "geckodriver")))]
561+
#[cfg(all(target_os = "windows", feature = "chromedriver"))]
560562
{
561563
if let Ok(output) = std::process::Command::new("where")
562564
.arg("chromedriver")

0 commit comments

Comments
 (0)