Create an `intern_string` function accessible to rust

This function will be used to intern paths to allow fast comparison and
fast hashing by pointer equality, improving the performance of depset
operations.

See the child CL's file.rs for usage.

In order to resolve a compile error on mac, we also patch in my changes to cxx.h from:
https://github.com/dtolnay/cxx/pull/1723
https://github.com/dtolnay/cxx/pull/1724

Hopefully they will be available upstream by the time we actually
vendor. Otherwise we'll just use a patched version of the crate like we
have for starlark-rs.

Bug: 528225104
Change-Id: I4f631049633dc5bd4c35a4f0125847566a6a6964
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/23260
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Commit-Queue: Matt Stark <msta@google.com>
diff --git a/build/gen.py b/build/gen.py
index c95b57e..675402b 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -347,7 +347,9 @@
     return escape_path_ninja('%s' % os.path.splitext(path)[0] + object_ext)
 
   def library_to_a(library):
-    return '%s%s' % (library, library_ext)
+    if platform.is_msvc():
+      return '%s%s' % (library, library_ext)
+    return 'lib%s%s' % (library, library_ext)
 
   ninja_lines = []
   def build_source(src_file, settings):
@@ -445,6 +447,7 @@
   libflags = os.environ.get('LIBFLAGS', '').split()
   include_dirs = [
       os.path.relpath(os.path.join(REPO_ROOT, 'src'), os.path.dirname(path)),
+      os.path.relpath(os.path.join(REPO_ROOT, 'src/gn/starlark/vendor/cxx/include'), os.path.dirname(path)),
       '.',
   ]
   if platform.is_zos():
@@ -535,6 +538,9 @@
     # flags not supported by gcc/g++.
     else:
       cflags.extend(['-Wrange-loop-analysis', '-Wextra-semi-stmt'])
+      # We use "extern C" to communicate with rust, not C. So it doesn't matter
+      # if the return type is an invalid C type.
+      cflags.append('-Wno-return-type-c-linkage')
 
     if platform.is_linux() or platform.is_mingw() or platform.is_msys():
       ldflags.append('-Wl,--as-needed')
@@ -618,6 +624,7 @@
         '/wd4099',
         '/wd4100',
         '/wd4127',
+        '/wd4190',
         '/wd4244',
         '/wd4267',
         '/wd4505',
@@ -665,6 +672,10 @@
         'src/base/value_iterators.cc',
         'src/base/values.cc',
       ]},
+      'string_atom': {'sources': [
+        'src/gn/string_atom.cc',
+        'src/gn/ffi/intern_string.cc',
+      ]},
       'gn_lib': {'sources': [
         'src/gn/action_target_generator.cc',
         'src/gn/action_values.cc',
@@ -797,7 +808,6 @@
         'src/gn/source_dir.cc',
         'src/gn/source_file.cc',
         'src/gn/standard_out.cc',
-        'src/gn/string_atom.cc',
         'src/gn/string_output_buffer.cc',
         'src/gn/string_utils.cc',
         'src/gn/substitution_list.cc',
diff --git a/src/gn/ffi/intern_string.cc b/src/gn/ffi/intern_string.cc
new file mode 100644
index 0000000..bd31cd4
--- /dev/null
+++ b/src/gn/ffi/intern_string.cc
@@ -0,0 +1,19 @@
+// Copyright 2026 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string_view>
+
+#include "cxx.h"
+#include "gn/string_atom.h"
+
+// `intern_string` is located in its own translation unit (`intern_string.cc`)
+// to prevent link-time dependencies on the `ctx` crate for standalone cargo
+// unit tests.
+//
+// By isolating `intern_string` in its own object file (`intern_string.o`), the
+// linker only pulls in this file and does not pull in `cxx_api.o`.
+extern "C" rust::Str intern_string(rust::Str s) {
+  StringAtom atom(std::string_view{s});
+  return rust::Str(atom.str());
+}
diff --git a/src/gn/starlark/vendor/cxx/include/cxx.h b/src/gn/starlark/vendor/cxx/include/cxx.h
index 4e261a3..1c97580 100644
--- a/src/gn/starlark/vendor/cxx/include/cxx.h
+++ b/src/gn/starlark/vendor/cxx/include/cxx.h
@@ -124,6 +124,9 @@
   Str(const std::string &);
   Str(const char *);
   Str(const char *, std::size_t);
+#if __cplusplus >= 201703L
+  Str(std::string_view s) : Str(s.data(), s.size()) {}
+#endif
 
   Str &operator=(const Str &) & noexcept = default;
 
@@ -243,6 +246,7 @@
   using iterator_category = std::random_access_iterator_tag;
 #endif
   using value_type = T;
+  using element_type = T;
   using difference_type = std::ptrdiff_t;
   using pointer = typename std::add_pointer<T>::type;
   using reference = typename std::add_lvalue_reference<T>::type;