diff --git a/crates/whisper_cpp/src/lib.rs b/crates/whisper_cpp/src/lib.rs index 55b542d..2749b1d 100644 --- a/crates/whisper_cpp/src/lib.rs +++ b/crates/whisper_cpp/src/lib.rs @@ -18,7 +18,7 @@ use whisper_cpp_sys::{ whisper_full_params__bindgen_ty_2, whisper_full_with_state, whisper_init_from_file_with_params_no_state, whisper_init_state, whisper_log_set, whisper_sampling_strategy_WHISPER_SAMPLING_BEAM_SEARCH, - whisper_sampling_strategy_WHISPER_SAMPLING_GREEDY, whisper_state, whisper_token, + whisper_sampling_strategy_WHISPER_SAMPLING_GREEDY, whisper_state, }; /// Boolean indicating if a logger has already been set using [`whisper_log_set`]. @@ -64,15 +64,19 @@ pub struct WhisperModel { } impl WhisperModel { - /// Loads a new *ggml* *whisper* model, given its file path. + /// Loads a new *ggml* *whisper* model, given its file path. If a device (GPU) index is + /// provided, the model is loaded into the GPU. #[doc(alias = "whisper_init_from_file_with_params_no_state")] - pub fn new_from_file

(model_path: P, use_gpu: bool) -> Result + pub fn new_from_file

(model_path: P, device: Option) -> Result where P: AsRef, { set_log(); - let params = whisper_context_params { use_gpu }; + let params = whisper_context_params { + use_gpu: device.is_some(), + gpu_device: device.unwrap_or(0) as i32, + }; let path_bytes = model_path .as_ref() @@ -152,7 +156,6 @@ pub enum WhisperSessionError { pub struct WhisperSession { context: Arc>, state: WhisperState, - prompt: Vec, } impl WhisperSession { @@ -171,7 +174,6 @@ impl WhisperSession { Ok(Self { context, state: WhisperState(state), - prompt: vec![], }) } @@ -237,13 +239,11 @@ impl WhisperSession { /// Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text. /// Uses the specified decoding strategy to obtain the text. #[doc(alias = "whisper_full_with_state")] - pub async fn full( + pub async fn advance( &mut self, - mut params: WhisperParams, + params: WhisperParams, samples: &[f32], ) -> Result<(), WhisperSessionError> { - // TODO use no_context from whisper_params instead - params.prompt_tokens = self.prompt.clone(); let locked = self.context.read().await; let res = unsafe { let (_vec, c_params) = params.c_params()?; @@ -260,15 +260,6 @@ impl WhisperSession { return Err(WhisperSessionError::Internal); } - let segments = self.segment_count(); - - for s in 0..segments { - let tokens = self.token_count(s); - for t in 0..tokens { - self.prompt.push(self.token_id(s, t)); - } - } - Ok(()) } @@ -305,6 +296,11 @@ impl WhisperSession { pub fn segment_text(&self, segment: u32) -> Result { let text = unsafe { let res = whisper_full_get_segment_text_from_state(self.state.0, segment as c_int); + + if res.is_null() { + return Err(WhisperSessionError::Internal); + } + CStr::from_ptr(res.cast_mut()) }; @@ -345,6 +341,17 @@ impl WhisperSession { pub fn token_probability(&self) { todo!() } + + /// Returns the decoded text of the last segment encoding. + pub fn new_context(&self) -> Result { + let mut res = "".to_string(); + + for i in 0..self.segment_count() { + res += &*self.segment_text(i)?; + } + + Ok(res) + } } #[derive(Debug, Error)] @@ -402,7 +409,7 @@ pub struct WhisperParams { translate: bool, /// Do not use past transcription (if any) as initial prompt for the decoder. - no_context: bool, + pub no_context: bool, /// Do not generate timestamps. no_timestamps: bool, diff --git a/crates/whisper_cpp_sys/build.rs b/crates/whisper_cpp_sys/build.rs index 0232c9d..e70c8a1 100644 --- a/crates/whisper_cpp_sys/build.rs +++ b/crates/whisper_cpp_sys/build.rs @@ -1,5 +1,5 @@ -use std::path::PathBuf; use std::{env, fs}; +use std::path::PathBuf; // TODO add feature compatibility checks @@ -15,7 +15,7 @@ fn main() { let mut config = cmake::Config::new(submodule_dir); config - .define("BUILD_SHARED_LIBS", "OFF") + .define("BUILD_SHARED_LIBS", "ON") .define("WHISPER_BUILD_EXAMPLES", "OFF") .define("WHISPER_BUILD_TESTS", "OFF") .define("WHISPER_NO_ACCELERATE", "ON") // TODO accelerate is used by default, but is causing issues atm, check why @@ -23,35 +23,42 @@ fn main() { #[cfg(not(feature = "avx"))] { - config.define("WHISPER_NO_AVX", "ON") + config.define("WHISPER_NO_AVX", "ON"); } #[cfg(not(feature = "avx2"))] { - config.define("WHISPER_NO_AVX2", "ON") + config.define("WHISPER_NO_AVX2", "ON"); } #[cfg(not(feature = "fma"))] { - config.define("WHISPER_NO_FMA", "ON") + config.define("WHISPER_NO_FMA", "ON"); } #[cfg(not(feature = "f16c"))] { - config.define("WHISPER_NO_F16C", "ON") + config.define("WHISPER_NO_F16C", "ON"); + } + + #[cfg(feature = "cuda")] + { + config.define("WHISPER_CUBLAS", "ON"); } let dst = config.build(); - println!( - "cargo:rustc-link-search=native={}/lib/static", - dst.display() - ); - println!( - "cargo:rustc-link-search=native={}/lib64/static", - dst.display() - ); - println!("cargo:rustc-link-lib=static=whisper"); + if cfg!(target_family = "windows") { + println!("cargo:rustc-link-search=native={}/bin", dst.display()); + println!( + "cargo:rustc-link-search=native={}/lib/static", + dst.display() + ); + println!("cargo:rustc-link-lib=dylib=whisper"); + } else { + println!("cargo:rustc-link-search=native={}/lib", dst.display()); + println!("cargo:rustc-link-lib=dylib=whisper"); + } let bindings = bindgen::Builder::default() .header(submodule_dir.join("ggml.h").to_string_lossy()) @@ -80,34 +87,26 @@ fn main() { #[cfg(feature = "compat")] mod compat { - use std::path::Path; + use std::collections::HashSet; + use std::env; + use std::fmt::{Display, Formatter}; + use std::path::{Path, PathBuf}; use std::process::Command; pub fn redefine_symbols(out_path: impl AsRef) { - // TODO this whole section is a bit hacky, could probably clean it up a bit, particularly the retrieval of symbols from the library files - // TODO do this for cuda if necessary - let whisper_lib_name = lib_name(); - let (nm_name, objcopy_name) = tool_names(); - println!("Modifying {whisper_lib_name}, symbols acquired via \"{nm_name}\" and modified via \"{objcopy_name}\""); + let (nm, objcopy) = tools(); + println!("Modifying {whisper_lib_name}, symbols acquired via \"{nm}\" and modified via \"{objcopy}\""); - let lib_path = out_path.as_ref().join("lib").join("static"); + let lib_path = if cfg!(target_family = "windows") { + out_path.as_ref().join("bin") + } else { + out_path.as_ref().join("lib") + }; // Modifying symbols exposed by the ggml library - let output = Command::new(nm_name) - .current_dir(&lib_path) - .arg(whisper_lib_name) - .args(["-p", "-P"]) - .output() - .expect("Failed to acquire symbols from the compiled library."); - if !output.status.success() { - panic!( - "An error has occurred while acquiring symbols from the compiled library ({})", - output.status - ); - } - let out_str = String::from_utf8_lossy(output.stdout.as_slice()); + let out_str = nm_symbols(&nm, whisper_lib_name, &lib_path); let symbols = get_symbols( &out_str, [ @@ -133,86 +132,137 @@ mod compat { }, ], ); - - let mut cmd = Command::new(objcopy_name); - cmd.current_dir(&lib_path); - for symbol in symbols { - cmd.arg(format!("--redefine-sym={symbol}=whisper_{symbol}")); - } - let status = cmd - .arg(whisper_lib_name) - .status() - .expect("Failed to modify global symbols from the ggml library."); - if !status.success() { - panic!( - "An error as occurred while modifying global symbols from library file ({})", - status - ); - } + objcopy_redefine(&objcopy, whisper_lib_name, "whisp_", symbols, &lib_path); } /// Returns *Whisper.cpp*'s compiled library name, based on the operating system. fn lib_name() -> &'static str { if cfg!(target_family = "windows") { - "whisper.lib" - } else if cfg!(target_family = "unix") { - "libwhisper.a" + "whisper.dll" + } else if cfg!(target_os = "linux") { + "libwhisper.so" + } else if cfg!(any( + target_os = "macos", + target_os = "ios", + target_os = "dragonfly" + )) { + "libwhisper.dylib" } else { println!("cargo:warning=Unknown target family, defaulting to Unix lib names"); - "libwhisper.a" + "libwhisper.so" } } - /// Returns the names of tools equivalent to [nm][nm] and [objcopy][objcopy]. + /// Returns [`Tool`]s equivalent to [nm][nm] and [objcopy][objcopy]. /// /// [nm]: https://www.man7.org/linux/man-pages/man1/nm.1.html /// [objcopy]: https://www.man7.org/linux/man-pages/man1/objcopy.1.html - fn tool_names() -> (&'static str, &'static str) { + fn tools() -> (Tool, Tool) { let nm_names; let objcopy_names; - if cfg!(target_family = "unix") { + let nm_help; + let objcopy_help; + if cfg!(target_os = "linux") { nm_names = vec!["nm", "llvm-nm"]; objcopy_names = vec!["objcopy", "llvm-objcopy"]; + nm_help = vec!["\"nm\" from GNU Binutils", "\"llvm-nm\" from LLVM"]; + objcopy_help = vec![ + "\"objcopy\" from GNU Binutils", + "\"llvm-objcopy\" from LLVM", + ]; + } else if cfg!(any( + target_os = "macos", + target_os = "ios", + target_os = "dragonfly" + )) { + nm_names = vec!["nm", "llvm-nm"]; + objcopy_names = vec!["llvm-objcopy"]; + nm_help = vec!["\"llvm-nm\" from LLVM 17"]; + objcopy_help = vec!["\"llvm-objcopy\" from LLVM 17"]; } else { nm_names = vec!["llvm-nm"]; objcopy_names = vec!["llvm-objcopy"]; + nm_help = vec!["\"llvm-nm\" from LLVM 17"]; + objcopy_help = vec!["\"llvm-objcopy\" from LLVM 17"]; } - let nm_name; + let nm_env = "NM_PATH"; + println!("cargo:rerun-if-env-changed={nm_env}"); + println!("Looking for \"nm\" or an equivalent tool"); + let nm_name = find_tool(&nm_names, nm_env).unwrap_or_else(move || { + panic_tool_help("nm", nm_env, &nm_help); + unreachable!("The function above should have panicked") + }); + + let objcopy_env = "OBJCOPY_PATH"; + println!("cargo:rerun-if-env-changed={objcopy_env}"); + println!("Looking for \"objcopy\" or an equivalent tool.."); + let objcopy_name = find_tool(&objcopy_names, objcopy_env).unwrap_or_else(move || { + panic_tool_help("objcopy", objcopy_env, &objcopy_help); + unreachable!("The function above should have panicked") + }); - if let Some(path) = option_env!("NM_PATH") { - nm_name = path; - } else { - println!("Looking for \"nm\" or an equivalent tool"); - nm_name = find_tool(&nm_names).expect( - "No suitable tool equivalent to \"nm\" has been found in \ - PATH, if one is already installed, either add it to PATH or set NM_PATH to its full path", - ); - } + (nm_name, objcopy_name) + } - let objcopy_name; - if let Some(path) = option_env!("OBJCOPY_PATH") { - objcopy_name = path; - } else { - println!("Looking for \"objcopy\" or an equivalent tool"); - objcopy_name = find_tool(&objcopy_names).expect("No suitable tool equivalent to \"objcopy\" has \ - been found in PATH, if one is already installed, either add it to PATH or set OBJCOPY_PATH to its full path"); - } + /// A command line tool name present in `PATH` or its full [`Path`]. + enum Tool { + /// The name of a tool present in `PATH`. + Name(&'static str), - (nm_name, objcopy_name) + /// The full [`Path`] to a tool. + FullPath(PathBuf), } - /// Returns the first tool found in the system, given a list of tool names, returning the first one found and - /// printing its version. + impl Display for Tool { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Tool::Name(name) => write!(f, "{}", name), + Tool::FullPath(path) => write!(f, "{}", path.display()), + } + } + } + + /// Returns the first [`Tool`] found in the system `PATH`, given a list of tool names, returning + /// the first one found and printing its version. + /// + /// If a value is present in the provided environment variable name, it will get checked + /// instead. /// - /// Returns [`Option::None`] if no tool is found. - fn find_tool<'a>(names: &[&'a str]) -> Option<&'a str> { + /// ## Panic + /// Returns [`Option::None`] if no [`Tool`] is found. + fn find_tool(names: &[&'static str], env: &str) -> Option { + if let Ok(path_str) = env::var(env) { + let path_str = path_str.trim_matches([' ', '"', '\''].as_slice()); + println!("{env} is set, checking if \"{path_str}\" is a valid tool"); + let path = PathBuf::from(&path_str); + + if !path.is_file() { + panic!("\"{path_str}\" is not a file path.") + } + + let output = Command::new(path_str) + .arg("--version") + .output() + .unwrap_or_else(|e| panic!("Failed to run \"{path_str} --version\". ({e})")); + + if output.status.success() { + let out_str = String::from_utf8_lossy(&output.stdout); + println!("Valid tool found:\n{out_str}"); + } else { + println!("cargo:warning=Tool \"{path_str}\" found, but could not execute \"{path_str} --version\"") + } + + return Some(Tool::FullPath(path)); + } + + println!("{env} not set, looking for {names:?} in PATH"); for name in names { if let Ok(output) = Command::new(name).arg("--version").output() { if output.status.success() { let out_str = String::from_utf8_lossy(&output.stdout); - println!("Valid \"tool\" found:\n{out_str}"); - return Some(name); + println!("Valid tool found:\n{out_str}"); + return Some(Tool::Name(name)); } } } @@ -220,6 +270,80 @@ mod compat { None } + /// Always panics, printing suggestions for finding the specified tool. + fn panic_tool_help(name: &str, env: &str, suggestions: &[&str]) { + let suggestions_str = if suggestions.is_empty() { + String::new() + } else { + let mut suggestions_str = "For your Operating System we recommend:\n".to_string(); + for suggestion in &suggestions[..suggestions.len() - 1] { + suggestions_str.push_str(&format!("{suggestion}\nOR\n")); + } + suggestions_str.push_str(suggestions[suggestions.len() - 1]); + suggestions_str + }; + + panic!("No suitable tool equivalent to \"{name}\" has been found in PATH, if one is already installed, either add its directory to PATH or set {env} to its full path. {suggestions_str}") + } + + /// Executes [nm][nm] or an equivalent tool in portable mode and returns the output. + /// + /// ## Panic + /// Will panic on any errors. + /// + /// [nm]: https://www.man7.org/linux/man-pages/man1/nm.1.html + fn nm_symbols(tool: &Tool, target_lib: &str, out_path: impl AsRef) -> String { + let output = Command::new(tool.to_string()) + .current_dir(&out_path) + .arg(target_lib) + .args(["-p", "-P"]) + .output() + .unwrap_or_else(move |e| panic!("Failed to run \"{tool}\". ({e})")); + + if !output.status.success() { + panic!( + "An error has occurred while acquiring symbols from the compiled library \"{target_lib}\" ({}):\n{}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + } + + String::from_utf8_lossy(&output.stdout).to_string() + } + + /// Executes [objcopy][objcopy], adding a prefix to the specified symbols of the target library. + /// + /// ## Panic + /// Will panic on any errors. + /// + /// [objcopy]: https://www.man7.org/linux/man-pages/man1/objcopy.1.html + fn objcopy_redefine( + tool: &Tool, + target_lib: &str, + prefix: &str, + symbols: HashSet<&str>, + out_path: impl AsRef, + ) { + let mut cmd = Command::new(tool.to_string()); + cmd.current_dir(&out_path); + for symbol in symbols { + cmd.arg(format!("--redefine-sym={symbol}={prefix}{symbol}")); + } + + let output = cmd + .arg(target_lib) + .output() + .unwrap_or_else(move |e| panic!("Failed to run \"{tool}\". ({e})")); + + if !output.status.success() { + panic!( + "An error has occurred while redefining symbols from library file \"{target_lib}\" ({}):\n{}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + } + } + /// A filter for a symbol in a library. struct Filter<'a> { prefix: &'a str, @@ -232,14 +356,15 @@ mod compat { fn get_symbols<'a, const N: usize>( nm_output: &'a str, filters: [Filter<'a>; N], - ) -> impl Iterator + 'a { - nm_output + ) -> HashSet<&'a str> { + let iter = nm_output .lines() .map(|symbol| { // Strip irrelevant information let mut stripped = symbol; while stripped.split(' ').count() > 2 { + // SAFETY: We just made sure ' ' is present above let idx = unsafe { stripped.rfind(' ').unwrap_unchecked() }; stripped = &stripped[..idx] } @@ -257,6 +382,9 @@ mod compat { } false }) - .map(|symbol| &symbol[..symbol.len() - 2]) // Strip the type, so only the symbol remains + .map(|symbol| &symbol[..symbol.len() - 2]); // Strip the type, so only the symbol remains + + // Filter duplicates + HashSet::from_iter(iter) } } diff --git a/crates/whisper_cpp_sys/src/lib.rs b/crates/whisper_cpp_sys/src/lib.rs index 452f5b0..5b05ada 100644 --- a/crates/whisper_cpp_sys/src/lib.rs +++ b/crates/whisper_cpp_sys/src/lib.rs @@ -1,4 +1,3 @@ - #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] diff --git a/crates/whisper_cpp_sys/thirdparty/whisper.cpp b/crates/whisper_cpp_sys/thirdparty/whisper.cpp index a01b2e0..3d42463 160000 --- a/crates/whisper_cpp_sys/thirdparty/whisper.cpp +++ b/crates/whisper_cpp_sys/thirdparty/whisper.cpp @@ -1 +1 @@ -Subproject commit a01b2e0971caabbe40ce993513c1462a47462234 +Subproject commit 3d4246384525824b1dc6efc86f86003c8c615295 diff --git a/crates/whisper_cpp_tests/Cargo.toml b/crates/whisper_cpp_tests/Cargo.toml index 7b3229b..90ed359 100644 --- a/crates/whisper_cpp_tests/Cargo.toml +++ b/crates/whisper_cpp_tests/Cargo.toml @@ -10,3 +10,6 @@ thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } wav = "1.0.0" whisper_cpp = { version = "^0.2.1", path = "../whisper_cpp", default-features = false, features = ["compat", "native"] } + +[features] +cuda = ["whisper_cpp/cuda"] diff --git a/crates/whisper_cpp_tests/src/lib.rs b/crates/whisper_cpp_tests/src/lib.rs index d2b6d7f..2baf417 100644 --- a/crates/whisper_cpp_tests/src/lib.rs +++ b/crates/whisper_cpp_tests/src/lib.rs @@ -17,7 +17,7 @@ mod tests { #[tokio::test] async fn it_works() -> Result<(), TestError> { let model_paths = { - let mut dir = std::env::var("WHISPER_TEST_MODEL_DIR").unwrap_or_else(|_| { + let dir = std::env::var("WHISPER_TEST_MODEL_DIR").unwrap_or_else(|_| { eprintln!( "WHISPER_TEST_MODEL environment variable not set. \ Please set this to the path to a Whisper GGUF model file for the test to run." @@ -26,11 +26,12 @@ mod tests { std::process::exit(0) }); - if !dir.ends_with('/') { - dir.push('/'); + let dir = std::path::Path::new(&dir); + + if !dir.is_dir() { + panic!("\"{}\" is not a directory", dir.to_string_lossy()); } - let dir = std::path::Path::new(&dir); let mut models = tokio::fs::read_dir(dir).await.unwrap(); let mut rv = vec![]; @@ -58,7 +59,13 @@ mod tests { }); for model_path_str in model_paths { - let model = WhisperModel::new_from_file(model_path_str, false)?; + let device = if cfg!(any(feature = "cuda")) { + Some(0) + } else { + None + }; + + let model = WhisperModel::new_from_file(model_path_str, device)?; let mut session = model.new_session().await?; @@ -72,14 +79,10 @@ mod tests { .map(|v| *v as f32 / 32768.) .collect(); - session.full(params, &samples).await?; - - let mut result = "".to_string(); - for i in 0..session.segment_count() { - result += &*session.segment_text(i)?; - } + session.advance(params, &samples).await?; + let result = session.new_context()?; - println!("{result}"); + println!("\n{result}\n"); } Ok(()) diff --git a/flake.lock b/flake.lock index d82c0cf..34aadc4 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1700140236, - "narHash": "sha256-OpukFO0rRG2hJzD+pCQq+nSWuT9dBL6DSvADQaUlmFg=", + "lastModified": 1707004164, + "narHash": "sha256-9Hr8onWtvLk5A8vCEkaE9kxA0D7PR62povFokM1oL5Q=", "owner": "cachix", "repo": "devenv", - "rev": "525d60c44de848a6b2dd468f6efddff078eb2af2", + "rev": "0e68853bb27981a4ffd7a7225b59ed84f7180fc7", "type": "github" }, "original": { @@ -31,11 +31,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1700461394, - "narHash": "sha256-lBpjEshdBxeuJwc4+vh4jbO3AmhXbiFrkdWy2pABAAc=", + "lastModified": 1706941198, + "narHash": "sha256-t6/qloMYdknVJ9a3QzjylQIZnQfgefJ5kMim50B7dwA=", "owner": "nix-community", "repo": "fenix", - "rev": "5ad1b10123ca40c9d983fb0863403fd97a06c0f8", + "rev": "28dbd8b43ea328ee708f7da538c63e03d5ed93c8", "type": "github" }, "original": { @@ -85,11 +85,11 @@ ] }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -162,16 +162,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1700403855, - "narHash": "sha256-Q0Uzjik9kUTN9pd/kp52XJi5kletBhy29ctBlAG+III=", + "lastModified": 1707238373, + "narHash": "sha256-WKxT0yLzWbFZwYi92lI0yWJpYtRaFSWHGX8QXzejapw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0c5678df521e1407884205fe3ce3cf1d7df297db", + "rev": "fb0c047e30b69696acc42e669d02452ca1b55755", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } @@ -223,11 +223,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1688056373, - "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "lastModified": 1704725188, + "narHash": "sha256-qq8NbkhRZF1vVYQFt1s8Mbgo8knj+83+QlL5LBnYGpI=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "rev": "ea96f0c05924341c551a797aaba8126334c505d2", "type": "github" }, "original": { @@ -248,11 +248,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1700247620, - "narHash": "sha256-+Xg0qZLbC9dZx0Z6JbaVHR/BklAr2I83dzKLB8r41c8=", + "lastModified": 1706875368, + "narHash": "sha256-KOBXxNurIU2lEmO6lR2A5El32X9x8ITt25McxKZ/Ew0=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "255eed40c45fcf108ba844b4ad126bdc4e7a18df", + "rev": "8f6a72871ec87ed53cfe43a09fb284168a284e7e", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b6607c0..9c2967b 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; systems.url = "github:nix-systems/default"; flake-utils = { url = "github:numtide/flake-utils";