Skip to content

Commit a00ac2e

Browse files
committed
ref: compilation module
1 parent 7533c48 commit a00ac2e

File tree

10 files changed

+251
-245
lines changed

10 files changed

+251
-245
lines changed

Cargo.toml

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ version = "0.1.0"
44
edition = "2021"
55

66
[features]
7-
default = [ ]
8-
xcodegen = [ "async", "dirs" ]
9-
daemon = [ "serial", "completion", "logging", "xcode", "async", "watcher", "proc", "xcodegen" ]
10-
lua = [ "mlua" ]
11-
serial = [ "serde", "serde_json", "serde_yaml" ]
12-
completion = [ "serial", "lazy_static", "regex" ]
13-
logging = [ "tracing", "tracing-appender", "tracing-subscriber" ]
14-
xcode = [ "serial" ]
15-
async = [ "tokio", "async-trait" ]
16-
proc = [ "libproc" ]
17-
watcher = [ "dirs", "notify", "wax" ]
7+
default = [ ]
8+
xcodegen = [ "async", "dirs" ]
9+
daemon = [ "serial", "compilation", "logging", "xcode", "async", "watcher", "proc", "xcodegen" ]
10+
lua = [ "mlua" ]
11+
serial = [ "serde", "serde_json", "serde_yaml" ]
12+
compilation = [ "serial", "lazy_static", "regex", "shell-words" ]
13+
logging = [ "tracing", "tracing-appender", "tracing-subscriber" ]
14+
xcode = [ ]
15+
async = [ "tokio", "async-trait" ]
16+
proc = [ "libproc" ]
17+
watcher = [ "dirs", "notify", "wax" ]
1818

1919
[lib]
2020
name = "xcodebase"
@@ -34,32 +34,36 @@ required-features = ["daemon"]
3434

3535
# Error Handling
3636
anyhow = "^1.0.42"
37+
# suffix-position pipeline
38+
tap = "1.0.1"
3739

3840
# Serialization Feature
39-
serde = { version = "1.0", features = ["derive"], optional = true }
40-
serde_json = { version = "1.0.79", optional = true }
41-
serde_yaml = { version = "0.8.23", optional = true }
41+
serde = { version = "1.0", features = ["derive"], optional = true }
42+
serde_json = { version = "1.0.79", optional = true }
43+
serde_yaml = { version = "0.8.23", optional = true }
4244

4345
# Async Runtime Feature
44-
tokio = { version = "1.17.0", features = ["full"], optional = true }
45-
async-trait = { version = "0.1.52", optional = true }
46+
tokio = { version = "1.17.0", features = ["full"], optional = true }
47+
tokio-util = { version = "0.7.1", features = ["codec"], optional = true }
48+
async-trait = { version = "0.1.52", optional = true }
4649

4750
# Logging Feature
48-
tracing = { version = "0.1.32", optional = true }
49-
tracing-subscriber = { version = "0.3.9", features = ["env-filter"], optional = true}
50-
tracing-appender = { version = "0.2.1", optional = true }
51+
tracing = { version = "0.1.32", optional = true }
52+
tracing-subscriber = { version = "0.3.9", features = ["env-filter"], optional = true}
53+
tracing-appender = { version = "0.2.1", optional = true }
5154

5255
# Filesystem watcher
53-
notify = { version = "5.0.0-pre.13", optional = true }
54-
dirs = { version = "4.0", optional = true }
55-
wax = { version = "0.4.0", optional = true }
56+
notify = { version = "5.0.0-pre.13", optional = true }
57+
dirs = { version = "4.0", optional = true }
58+
wax = { version = "0.4.0", optional = true }
59+
60+
# Build Logs/Command Compilation feature
61+
regex = { version = "1.5", optional = true }
62+
lazy_static = { version = "1.4.0", optional = true }
63+
shell-words = { version = "1.1.0", optional = true }
64+
mlua = { version = "0.7.4", optional = true }
5665

57-
# Build logs Completion feature
58-
regex = { version = "1.5", optional = true }
59-
lazy_static = { version = "1.4.0", optional = true }
60-
shell-words = { version = "1.1.0" }
61-
mlua = { version = "0.7.4", optional = true }
6266

6367
# Proc feature: processes, processors, disks, components and networks
6468
# sysinfo = "0.23.5"
65-
libproc = { version = "0.10.0", optional = true }
69+
libproc = { version = "0.10.0", optional = true }

src/compile.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
mod command;
2+
pub use command::CompileCommand;
3+
4+
use crate::util::regex::matches_compile_swift_sources;
5+
use anyhow::{Context, Result};
6+
use serde::Deserialize;
7+
use std::path::PathBuf;
8+
use tap::Pipe;
9+
10+
// TODO: Support compiling commands for objective-c files
11+
// TODO: Test multiple module command compile
12+
13+
#[derive(Debug, Deserialize)]
14+
pub struct CompileCommands(Vec<CompileCommand>);
15+
16+
impl CompileCommands {
17+
pub fn from_logs(lines: Vec<String>) -> Self {
18+
// TODO: support index store
19+
let mut _index_store_path = Vec::default();
20+
let mut commands = vec![];
21+
let mut cursor = 0;
22+
23+
for line in lines.iter() {
24+
cursor += 1;
25+
if !line.starts_with("===") {
26+
continue;
27+
}
28+
29+
if matches_compile_swift_sources(line) {
30+
if let Some(command) = CompileCommand::swift_module(&lines, cursor) {
31+
if let Some(ref index_store_path) = command.index_store_path {
32+
_index_store_path.push(index_store_path.clone());
33+
}
34+
commands.push(command);
35+
}
36+
}
37+
}
38+
39+
Self(commands)
40+
}
41+
42+
pub fn from_file(path: &PathBuf) -> Result<Self> {
43+
std::fs::read_to_string(path)?
44+
.pipe_ref(|s| serde_json::from_str(s))
45+
.context("Deserialize .compile")
46+
}
47+
48+
/// Generate and write compile commands from build logs to directory
49+
#[cfg(feature = "async")]
50+
pub async fn update(dir: &PathBuf, build_log: Vec<String>) -> Result<()> {
51+
tracing::info!("Updating .compile in {:?}", dir);
52+
Self::from_logs(build_log)
53+
.pipe(|cmd| serde_json::to_vec_pretty(&cmd.0))?
54+
.pipe(|json| tokio::fs::write(dir.join(".compile"), json))
55+
.await
56+
.context("Write CompileCommands")
57+
}
58+
}
59+
60+
#[test]
61+
fn test() {
62+
tokio::runtime::Runtime::new().unwrap().block_on(async {
63+
use tap::Pipe;
64+
tokio::fs::read_to_string("/Users/tami5/repos/swift/wordle/build.log")
65+
.await
66+
.unwrap()
67+
.split("\n")
68+
.map(|l| l.to_string())
69+
.collect::<Vec<_>>()
70+
.pipe(CompileCommands::from_logs)
71+
.pipe(|v| println!("{:#?}", v));
72+
});
73+
}

src/compile/command.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// See https://clang.llvm.org/docs/JSONCompilationDatabase.html
2+
// See https://github.com/apple/sourcekit-lsp/blob/main/Sources/SKCore/CompilationDatabase.swift
3+
4+
use serde::{Deserialize, Serialize};
5+
6+
use crate::util::regex::matches_compile_swift_sources;
7+
8+
#[derive(Debug, Serialize, Deserialize, Clone)]
9+
pub struct CompileCommand {
10+
/// Module name
11+
/// NOTE: not sure if this required
12+
#[serde(
13+
rename(serialize = "module_name"),
14+
skip_serializing_if = "String::is_empty"
15+
)]
16+
pub name: String,
17+
18+
/// The path of the main file for the compilation, which may be relative to `directory`.
19+
#[serde(skip_serializing_if = "String::is_empty")]
20+
pub file: String,
21+
22+
/// The wroking directory for the compilation
23+
pub directory: String,
24+
25+
/// The compile command, this is alias with commandLine or split form of command
26+
pub command: String,
27+
28+
/// Source code files.
29+
#[serde(rename(serialize = "fileLists"), skip_serializing_if = "Vec::is_empty")]
30+
pub files: Vec<String>,
31+
32+
/// For SwiftFileList
33+
#[serde(rename(serialize = "fileLists"))]
34+
pub file_lists: Vec<String>,
35+
36+
/// The name of the build output
37+
#[serde(skip_serializing_if = "String::is_empty")]
38+
pub output: String,
39+
40+
/// Index store path. Kept for the caller to further process.
41+
#[serde(skip)]
42+
pub index_store_path: Option<String>,
43+
}
44+
45+
impl CompileCommand {
46+
pub fn can_parse(line: &String) -> bool {
47+
matches_compile_swift_sources(line)
48+
}
49+
50+
/// Parse starting from current line as swift module
51+
/// Matching r"^CompileSwiftSources\s*"
52+
pub fn swift_module(lines: &Vec<String>, cursor: usize) -> Option<CompileCommand> {
53+
let directory = match lines.get(cursor) {
54+
Some(s) => s.trim().replace("cd ", ""),
55+
None => {
56+
tracing::error!("Found COMPILE_SWIFT_MODULE_PATERN but no more lines");
57+
return None;
58+
}
59+
};
60+
61+
let command = match lines.get(cursor) {
62+
Some(s) => s.trim().to_string(),
63+
None => {
64+
tracing::error!("Found COMPILE_SWIFT_MODULE_PATERN but couldn't extract command");
65+
return None;
66+
}
67+
};
68+
69+
let arguments = match shell_words::split(&command) {
70+
Ok(l) => l,
71+
Err(e) => {
72+
tracing::error!("Fail to create swift module command {e}");
73+
return None;
74+
}
75+
};
76+
77+
// NOTE: This is never changed
78+
let file = String::default();
79+
let output = String::default();
80+
let mut name = String::default();
81+
let mut files = Vec::default();
82+
let mut file_lists = Vec::default();
83+
let mut index_store_path = None;
84+
85+
for i in 0..arguments.len() {
86+
let val = &arguments[i];
87+
if val == "-module-name" {
88+
name = arguments[i + 1].to_owned();
89+
} else if val == "-index-store-path" {
90+
index_store_path = Some(arguments[i + 1].to_owned());
91+
} else if val.ends_with(".swift") {
92+
files.push(val.to_owned());
93+
} else if val.ends_with(".SwiftFileList") {
94+
file_lists.push(val.replace("@", "").to_owned());
95+
}
96+
}
97+
98+
let command = Self {
99+
directory,
100+
command: arguments.join(" "),
101+
name,
102+
file,
103+
output,
104+
files,
105+
file_lists,
106+
index_store_path,
107+
};
108+
109+
tracing::debug!("Got Swift commands for {}", command.directory);
110+
tracing::trace!("{:#?}", command);
111+
Some(command)
112+
}
113+
}

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ pub use state::*;
99
#[cfg(feature = "xcode")]
1010
pub mod xcode;
1111

12+
#[cfg(feature = "compilation")]
13+
pub mod compile;
14+
1215
#[cfg(feature = "xcodegen")]
1316
pub mod xcodegen;
1417

@@ -24,3 +27,9 @@ pub use util::tracing::install_tracing;
2427

2528
#[cfg(feature = "watcher")]
2629
pub use util::watch;
30+
31+
#[cfg(feature = "server")]
32+
pub mod server;
33+
34+
#[cfg(feature = "server")]
35+
pub use server::*;

src/util/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
pub mod mlua;
33
#[cfg(feature = "proc")]
44
pub mod proc;
5+
#[cfg(feature = "regex")]
6+
pub mod regex;
57
#[cfg(feature = "logging")]
68
pub mod tracing;
79
#[cfg(feature = "watcher")]

src/util/regex.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use lazy_static::lazy_static;
2+
use regex::Regex;
3+
4+
#[cfg(feature = "compilation")]
5+
lazy_static! {
6+
static ref COMPILE_SWIFT_SOURCES: Regex = Regex::new(r"^CompileSwiftSources\s*").unwrap();
7+
static ref COMPILE_SWIFT: Regex = Regex::new(r"^CompileSwift\s+ \w+\s+ \w+\s+ (.+)$").unwrap();
8+
}
9+
10+
#[cfg(feature = "compilation")]
11+
pub fn matches_compile_swift_sources(text: &str) -> bool {
12+
COMPILE_SWIFT_SOURCES.is_match(text)
13+
}
14+
15+
#[cfg(feature = "compilation")]
16+
pub fn matches_compile_swift(text: &str) -> bool {
17+
COMPILE_SWIFT.is_match(text)
18+
}

src/xcode/actions.rs renamed to src/xcode.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use anyhow::Result;
2+
use serde_json::json;
23
use std::ffi;
34
use std::fmt::Debug;
45
use std::path::{Path, PathBuf};
56
use std::process::{ExitStatus, Stdio};
6-
use tokio::process::Command;
7-
8-
use super::compilation::Compiliation;
9-
use serde_json::json;
107
use tokio::fs;
118
use tokio::io::AsyncWriteExt;
9+
use tokio::process::Command;
1210

1311
// https://github.com/Gordon-F/cargo-xcodebuild
1412
/// run xcodebuild build with extra arguments
@@ -65,14 +63,6 @@ where
6563
.await
6664
}
6765

68-
#[cfg(feature = "serial")]
69-
pub async fn update_compiled_commands(root: &PathBuf, build_log: Vec<String>) -> Result<()> {
70-
let path = root.join(".compile");
71-
tracing::info!("Updating {:?}", path);
72-
fs::write(path, Compiliation::new(build_log).to_json()?).await?;
73-
Ok(())
74-
}
75-
7666
pub async fn ensure_server_config_file(root: &PathBuf) -> Result<()> {
7767
let path = root.join("buildServer.json");
7868
if fs::File::open(&path).await.is_ok() {
@@ -85,7 +75,7 @@ pub async fn ensure_server_config_file(root: &PathBuf) -> Result<()> {
8575
let config = json! ({
8676
"name": "XcodeBase Server",
8777
// FIXME: Point to user xcode-build-server
88-
"argv": ["/Users/tami5/repos/neovim/XcodeBase.nvim/target/debug/xcodebase-build-server"],
78+
"argv": ["/Users/tami5/repos/neovim/XcodeBase.nvim/target/debug/xcodebase-server"],
8979
"version": "0.1",
9080
"bspVersion": "0.2",
9181
"languages": [
@@ -94,7 +84,7 @@ pub async fn ensure_server_config_file(root: &PathBuf) -> Result<()> {
9484
"objective-cpp",
9585
"c",
9686
"cpp"
97-
],
87+
]
9888
});
9989

10090
file.write_all(config.to_string().as_ref()).await?;

0 commit comments

Comments
 (0)