@@ -253,15 +253,46 @@ publish_compile_commands(
253253 " fresh compile database for '{}' is not a JSON array" , path.string ())));
254254 }
255255
256+ std::filesystem::path publishPath = path;
257+ std::error_code statusEc;
258+ const bool isLink = std::filesystem::is_symlink (path, statusEc);
259+ if (statusEc) {
260+ return std::unexpected (write_error (std::format (
261+ " cannot inspect compile database '{}': {}" , path.string (),
262+ statusEc.message ())));
263+ }
264+ if (isLink) {
265+ auto target = std::filesystem::read_symlink (path, statusEc);
266+ if (statusEc) {
267+ return std::unexpected (write_error (std::format (
268+ " cannot resolve compile database link '{}': {}" , path.string (),
269+ statusEc.message ())));
270+ }
271+ publishPath = target.is_absolute () ? target : path.parent_path () / target;
272+ }
273+
256274 std::optional<std::string> existing;
257- if (std::ifstream is (path, std::ios::binary); is) {
275+ std::ifstream input (publishPath, std::ios::binary);
276+ if (input) {
258277 std::stringstream ss;
259- ss << is .rdbuf ();
260- if (is .bad ()) {
278+ ss << input .rdbuf ();
279+ if (input .bad ()) {
261280 return std::unexpected (write_error (std::format (
262- " cannot read existing compile database '{}'" , path .string ())));
281+ " cannot read existing compile database '{}'" , publishPath .string ())));
263282 }
264283 existing = ss.str ();
284+ } else {
285+ std::error_code existsEc;
286+ auto exists = std::filesystem::exists (publishPath, existsEc);
287+ if (existsEc) {
288+ return std::unexpected (write_error (std::format (
289+ " cannot inspect existing compile database '{}': {}" ,
290+ publishPath.string (), existsEc.message ())));
291+ }
292+ if (exists) {
293+ return std::unexpected (write_error (std::format (
294+ " cannot read existing compile database '{}'" , publishPath.string ())));
295+ }
265296 }
266297
267298 // 完全相同的有效输入不重写文件,避免 clangd 因 mtime 变化重复索引。
@@ -288,9 +319,12 @@ publish_compile_commands(
288319 }
289320
290321 static std::atomic<std::uint64_t > sequence{0 };
291- auto temp = path.parent_path ()
292- / std::format (" .{}.tmp.{}.{}" , path.filename ().string (),
322+ const auto nonce = std::random_device{}();
323+ // 临时文件和链接目标同目录,避免 rename 跨文件系统;随机量降低跨进程碰撞概率。
324+ auto temp = publishPath.parent_path ()
325+ / std::format (" .{}.tmp.{}.{}.{}" , publishPath.filename ().string (),
293326 std::chrono::steady_clock::now ().time_since_epoch ().count (),
327+ static_cast <unsigned long long >(nonce),
294328 sequence.fetch_add (1 , std::memory_order_relaxed));
295329 auto cleanup_temp = [&] {
296330 std::error_code cleanupEc;
@@ -318,10 +352,10 @@ publish_compile_commands(
318352 }
319353
320354 std::error_code ec;
321- if (!replaceFile (temp, path , ec)) {
355+ if (!replaceFile (temp, publishPath , ec)) {
322356 cleanup_temp ();
323357 return std::unexpected (write_error (std::format (
324- " cannot replace '{}': {}" , path .string (), ec.message ())));
358+ " cannot replace '{}': {}" , publishPath .string (), ec.message ())));
325359 }
326360
327361 return CompileCommandsWriteResult{true , finalJson.size ()};
0 commit comments