Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to specify server port from cli #2239

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ sccache supports gcc, clang, MSVC, rustc, [NVCC](https://docs.nvidia.com/cuda/cu

If you don't [specify otherwise](#storage-options), sccache will use a local disk cache.

sccache works using a client-server model, where the server runs locally on the same machine as the client. The client-server model allows the server to be more efficient by keeping some state in memory. The sccache command will spawn a server process if one is not already running, or you can run `sccache --start-server` to start the background server process without performing any compilation.
sccache works using a client-server model, where the server runs locally on the same machine as the client. The client-server model allows the server to be more efficient by keeping some state in memory. The sccache command will spawn a server process if one is not already running, or you can run `sccache --start-server[=port]` to start the background server process without performing any compilation.
sylvestre marked this conversation as resolved.
Show resolved Hide resolved

You can run `sccache --stop-server` to terminate the server. It will also terminate after (by default) 10 minutes of inactivity.
You can run `sccache --stop-server[=port]` to terminate the server. It will also terminate after (by default) 10 minutes of inactivity.

Running `sccache --show-stats` will print a summary of cache statistics.

Expand Down
28 changes: 20 additions & 8 deletions src/cmdline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ pub enum Command {
/// Run background server.
InternalStartServer,
/// Start background server as a subprocess.
StartServer,
StartServer(Option<u16>),
/// Stop background server.
StopServer,
StopServer(Option<u16>),
/// Zero cache statistics and exit.
ZeroStats,
/// Show the status of the distributed client.
Expand Down Expand Up @@ -136,13 +136,19 @@ fn get_clap_command() -> clap::Command {
.action(ArgAction::SetTrue),
flag_infer_long("start-server")
.help("start background server")
.action(ArgAction::SetTrue),
.value_name("port")
.num_args(0..=1)
.default_missing_value("None")
.action(ArgAction::Append),
flag_infer_long("debug-preprocessor-cache")
.help("show all preprocessor cache entries")
.action(ArgAction::SetTrue),
flag_infer_long("stop-server")
.help("stop background server")
.action(ArgAction::SetTrue),
.value_name("port")
.num_args(0..=1)
.default_missing_value("None")
.action(ArgAction::Append),
flag_infer_long_and_short("zero-stats")
.help("zero statistics counters")
.action(ArgAction::SetTrue),
Expand Down Expand Up @@ -268,12 +274,18 @@ pub fn try_parse() -> Result<Command> {
.cloned()
.expect("There is a default value");
Ok(Command::ShowStats(fmt, true))
} else if matches.get_flag("start-server") {
Ok(Command::StartServer)
} else if matches.contains_id("start-server") {
let port = matches
.get_one::<String>("start-server")
.and_then(|s| s.parse::<u16>().ok());
Ok(Command::StartServer(port))
} else if matches.get_flag("debug-preprocessor-cache") {
Ok(Command::DebugPreprocessorCacheEntries)
} else if matches.get_flag("stop-server") {
Ok(Command::StopServer)
} else if matches.contains_id("stop-server") {
let port = matches
.get_one::<String>("stop-server")
.and_then(|s| s.parse::<u16>().ok());
Ok(Command::StopServer(port))
} else if matches.get_flag("zero-stats") {
Ok(Command::ZeroStats)
} else if matches.get_flag("dist-auth") {
Expand Down
56 changes: 43 additions & 13 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::jobserver::Client;
use crate::mock_command::{CommandChild, CommandCreatorSync, ProcessCommandCreator, RunCommand};
use crate::protocol::{Compile, CompileFinished, CompileResponse, Request, Response};
use crate::server::{self, DistInfo, ServerInfo, ServerStartup, ServerStats};
use crate::util::daemonize;
use crate::util::{self, daemonize};
use byteorder::{BigEndian, ByteOrder};
use fs::{File, OpenOptions};
use fs_err as fs;
Expand Down Expand Up @@ -51,10 +51,22 @@ const SERVER_STARTUP_TIMEOUT: Duration = Duration::from_millis(10000);

/// Get the port on which the server should listen.
fn get_port() -> u16 {
env::var("SCCACHE_SERVER_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(DEFAULT_PORT)
let fallback = || -> u16 {
env::var("SCCACHE_SERVER_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(
util::get_from_client_cache("recent_port")
.ok()
.and_then(|port| port)
.and_then(|port| port.parse().ok())
.unwrap_or(DEFAULT_PORT),
)
};
match &Config::load() {
Ok(config) => config.port.unwrap_or_else(fallback),
Err(_) => fallback(),
}
}

/// Check if ignoring all response errors
Expand Down Expand Up @@ -82,7 +94,10 @@ async fn read_server_startup_status<R: AsyncReadExt + Unpin>(
/// Re-execute the current executable as a background server, and wait
/// for it to start up.
#[cfg(not(windows))]
fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup> {
fn run_server_process(
startup_timeout: Option<Duration>,
cli_port: Option<u16>,
) -> Result<ServerStartup> {
trace!("run_server_process");
let tempdir = tempfile::Builder::new().prefix("sccache").tempdir()?;
let socket_path = tempdir.path().join("sock");
Expand All @@ -98,11 +113,17 @@ fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup
tokio::net::UnixListener::bind(&socket_path)?
};

let port = cli_port.unwrap_or_else(get_port);
if cli_port.is_some() {
util::write_to_client_cache("recent_port", port.to_string().as_str())?;
}

let _child = process::Command::new(&exe_path)
.current_dir(workdir)
.env("SCCACHE_START_SERVER", "1")
.env("SCCACHE_STARTUP_NOTIFY", &socket_path)
.env("RUST_BACKTRACE", "1")
.env("SCCACHE_SERVER_PORT", port.to_string())
.spawn()?;

let startup = async move {
Expand Down Expand Up @@ -168,7 +189,10 @@ fn redirect_error_log(f: File) -> Result<()> {

/// Re-execute the current executable as a background server.
#[cfg(windows)]
fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup> {
fn run_server_process(
startup_timeout: Option<Duration>,
_port: Option<u16>,
) -> Result<ServerStartup> {
use futures::StreamExt;
use std::mem;
use std::os::windows::ffi::OsStrExt;
Expand All @@ -188,6 +212,7 @@ fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup
let pipe_name = &format!(r"\\.\pipe\{}", Uuid::new_v4().as_simple());

// Spawn a server which should come back and connect to us
let port = _port.unwrap_or_else(get_port);
let exe_path = env::current_exe()?;
let mut exe = OsStr::new(&exe_path)
.encode_wide()
Expand All @@ -202,6 +227,10 @@ fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup
OsString::from(&pipe_name),
),
(OsString::from("RUST_BACKTRACE"), OsString::from("1")),
(
OsString::from("SCCACHE_SERVER_PORT"),
OsString::from(port.to_string()),
),
];
for (key, val) in env::vars_os().chain(extra_vars) {
v.extend(
Expand Down Expand Up @@ -307,7 +336,7 @@ fn connect_or_start_server(
{
// If the connection was refused we probably need to start
// the server.
match run_server_process(startup_timeout)? {
match run_server_process(startup_timeout, Some(port))? {
ServerStartup::Ok { port: actualport } => {
if port != actualport {
// bail as the next connect_with_retry will fail
Expand Down Expand Up @@ -660,11 +689,11 @@ pub fn run_command(cmd: Command) -> Result<i32> {
}
server::start_server(config, get_port())?;
}
Command::StartServer => {
Command::StartServer(_port) => {
trace!("Command::StartServer");
println!("sccache: Starting the server...");
let startup =
run_server_process(startup_timeout).context("failed to start server process")?;
let startup = run_server_process(startup_timeout, _port)
.context("failed to start server process")?;
match startup {
ServerStartup::Ok { port } => {
if port != DEFAULT_PORT {
Expand All @@ -676,10 +705,11 @@ pub fn run_command(cmd: Command) -> Result<i32> {
ServerStartup::Err { reason } => bail!("Server startup failed: {}", reason),
}
}
Command::StopServer => {
Command::StopServer(port) => {
trace!("Command::StopServer");
println!("Stopping sccache server...");
let server = connect_to_server(get_port()).context("couldn't connect to server")?;
let server = connect_to_server(port.unwrap_or_else(get_port))
.context("couldn't connect to server")?;
let stats = request_shutdown(server)?;
stats.print(false);
}
Expand Down
22 changes: 22 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ pub struct FileConfig {
pub cache: CacheConfigs,
pub dist: DistConfig,
pub server_startup_timeout_ms: Option<u64>,
pub port: Option<u16>,
}

// If the file doesn't exist or we can't read it, log the issue and proceed. If the
Expand Down Expand Up @@ -946,6 +947,7 @@ pub struct Config {
pub fallback_cache: DiskCacheConfig,
pub dist: DistConfig,
pub server_startup_timeout: Option<std::time::Duration>,
pub port: Option<u16>,
}

impl Config {
Expand All @@ -967,6 +969,7 @@ impl Config {
cache,
dist,
server_startup_timeout_ms,
port,
} = file_conf;
conf_caches.merge(cache);

Expand All @@ -982,6 +985,7 @@ impl Config {
fallback_cache,
dist,
server_startup_timeout,
port,
}
}
}
Expand Down Expand Up @@ -1281,6 +1285,7 @@ fn config_overrides() {
},
dist: Default::default(),
server_startup_timeout_ms: None,
port: None,
};

assert_eq!(
Expand All @@ -1303,6 +1308,7 @@ fn config_overrides() {
},
dist: Default::default(),
server_startup_timeout: None,
port: None,
}
);
}
Expand Down Expand Up @@ -1578,6 +1584,22 @@ no_credentials = true
rewrite_includes_only: false,
},
server_startup_timeout_ms: Some(10000),
port: None,
}
)
}

#[test]
fn test_port_config() {
const CONFIG_STR: &str = "port = 8080";
let file_config: FileConfig = toml::from_str(CONFIG_STR).expect("Is valid toml.");
assert_eq!(
file_config,
FileConfig {
cache: Default::default(),
dist: Default::default(),
server_startup_timeout_ms: None,
port: Some(8080),
}
)
}
38 changes: 37 additions & 1 deletion src/test/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ fn test_server_compile() {
// https://github.com/mozilla/sccache/issues/234
#[cfg(not(target_os = "macos"))]
fn test_server_port_in_use() {
// Bind an arbitrary free port.
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let sccache = find_sccache_binary();
let output = Command::new(sccache)
Expand All @@ -320,3 +319,40 @@ fn test_server_port_in_use() {
s
);
}

#[test]
#[serial]
// test fails intermittently on macos:
// https://github.com/mozilla/sccache/issues/234
#[cfg(not(target_os = "macos"))]
fn test_server_port_from_cli() {
let sccache = find_sccache_binary();
loop {
let port = 10_000 + rand::random::<u16>() % 30_000;
let output = Command::new(sccache.clone())
.arg("--start-server")
.arg(port.to_string())
.output()
.unwrap();
let s = String::from_utf8_lossy(&output.stderr);
const PORT_IN_USE: &str = "Address in use";
if s.contains(PORT_IN_USE) {
continue;
}
assert!(output.status.success());
break;
}
// try to compile something to ensure our compile requests use the most recent port
let output = Command::new(sccache.clone())
.arg("gcc")
.arg("-c")
.arg("test.c")
.arg("-o")
.arg("test.o")
.output()
.unwrap();
assert!(output.status.success());

let output = Command::new(sccache).arg("--stop-server").output().unwrap();
assert!(output.status.success());
}
Loading
Loading