autocxx: Support multiple AUTOCXX_RS_JSON_ARCHIVE entries

Backport of https://github.com/google/autocxx/pull/1147.

Change-Id: I1657b15cff87110a177eecf49944eabc9b628c89
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
diff --git a/third_party/autocxx/gen/cmd/src/main.rs b/third_party/autocxx/gen/cmd/src/main.rs
index 8b109c3..f8db241 100644
--- a/third_party/autocxx/gen/cmd/src/main.rs
+++ b/third_party/autocxx/gen/cmd/src/main.rs
@@ -74,7 +74,11 @@
 instead of
   --gen-rs-include
 and you will need to give AUTOCXX_RS_JSON_ARCHIVE when building the Rust code.
-The output filename is named gen.rs.json.
+The output filename is named gen.rs.json. AUTOCXX_RS_JSON_ARCHIVE should be set
+to the path to gen.rs.json. It may optionally have multiple paths separated the
+way as the PATH environment variable for the current platform, see
+[`std::env::split_paths`] for details. The first path which is successfully
+opened will be used.
 
 This teaches rustc (and the autocxx macro) that all the different Rust bindings
 for multiple different autocxx macros have been archived into this single file.
diff --git a/third_party/autocxx/gen/cmd/tests/cmd_test.rs b/third_party/autocxx/gen/cmd/tests/cmd_test.rs
index 7e455a2..6fd3382 100644
--- a/third_party/autocxx/gen/cmd/tests/cmd_test.rs
+++ b/third_party/autocxx/gen/cmd/tests/cmd_test.rs
@@ -147,6 +147,58 @@
 }
 
 #[test]
+fn test_gen_archive_first_entry() -> Result<(), Box<dyn std::error::Error>> {
+    let tmp_dir = tempdir()?;
+    base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
+    File::create(tmp_dir.path().join("cxx.h"))
+        .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
+    let r = build_from_folder(
+        tmp_dir.path(),
+        &tmp_dir.path().join("demo/main.rs"),
+        vec![tmp_dir.path().join("gen.rs.json")],
+        &["gen0.cc"],
+        RsFindMode::Custom(Box::new(|path: &Path| {
+            std::env::set_var(
+                "AUTOCXX_RS_JSON_ARCHIVE",
+                std::env::join_paths([&path.join("gen.rs.json"), Path::new("/nonexistent")])
+                    .unwrap(),
+            )
+        })),
+    );
+    if KEEP_TEMPDIRS {
+        println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
+    }
+    r.unwrap();
+    Ok(())
+}
+
+#[test]
+fn test_gen_archive_second_entry() -> Result<(), Box<dyn std::error::Error>> {
+    let tmp_dir = tempdir()?;
+    base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
+    File::create(tmp_dir.path().join("cxx.h"))
+        .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
+    let r = build_from_folder(
+        tmp_dir.path(),
+        &tmp_dir.path().join("demo/main.rs"),
+        vec![tmp_dir.path().join("gen.rs.json")],
+        &["gen0.cc"],
+        RsFindMode::Custom(Box::new(|path: &Path| {
+            std::env::set_var(
+                "AUTOCXX_RS_JSON_ARCHIVE",
+                std::env::join_paths([Path::new("/nonexistent"), &path.join("gen.rs.json")])
+                    .unwrap(),
+            )
+        })),
+    );
+    if KEEP_TEMPDIRS {
+        println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
+    }
+    r.unwrap();
+    Ok(())
+}
+
+#[test]
 fn test_gen_multiple_in_archive() -> Result<(), Box<dyn std::error::Error>> {
     let tmp_dir = tempdir()?;
 
diff --git a/third_party/autocxx/integration-tests/src/lib.rs b/third_party/autocxx/integration-tests/src/lib.rs
index 352c8ab..f4667d5 100644
--- a/third_party/autocxx/integration-tests/src/lib.rs
+++ b/third_party/autocxx/integration-tests/src/lib.rs
@@ -57,6 +57,9 @@
     AutocxxRs,
     AutocxxRsArchive,
     AutocxxRsFile,
+    /// This just calls the callback instead of setting any environment variables. The callback
+    /// receives the path to the temporary directory.
+    Custom(Box<dyn FnOnce(&Path)>),
 }
 
 /// API to test building pre-generated files.
@@ -174,6 +177,7 @@
                 "AUTOCXX_RS_FILE",
                 self.temp_dir.path().join("gen0.include.rs"),
             ),
+            RsFindMode::Custom(f) => f(self.temp_dir.path()),
         };
         std::panic::catch_unwind(|| {
             let test_cases = trybuild::TestCases::new();
diff --git a/third_party/autocxx/parser/src/file_locations.rs b/third_party/autocxx/parser/src/file_locations.rs
index a113d0f..6b3c58a 100644
--- a/third_party/autocxx/parser/src/file_locations.rs
+++ b/third_party/autocxx/parser/src/file_locations.rs
@@ -95,13 +95,13 @@
                     include!( #fname );
                 }
             }
-            FileLocationStrategy::FromAutocxxRsJsonArchive(fname) => {
-                let archive = File::open(fname).unwrap_or_else(|_| panic!("Unable to open {}. This may mean you didn't run the codegen tool (autocxx_gen) before building the Rust code.", fname.to_string_lossy()));
+            FileLocationStrategy::FromAutocxxRsJsonArchive(fnames) => {
+                let archive = std::env::split_paths(fnames).flat_map(File::open).next().unwrap_or_else(|| panic!("Unable to open any of the paths listed in {}. This may mean you didn't run the codegen tool (autocxx_gen) before building the Rust code.", fnames.to_string_lossy()));
                 let multi_bindings: MultiBindings = serde_json::from_reader(archive)
                     .unwrap_or_else(|_| {
-                        panic!("Unable to interpret {} as JSON", fname.to_string_lossy())
+                        panic!("Unable to interpret {} as JSON", fnames.to_string_lossy())
                     });
-                multi_bindings.get(config).unwrap_or_else(|err| panic!("Unable to find a suitable set of bindings within the JSON archive {} ({}). This likely means that the codegen tool hasn't been rerun since some changes in your include_cpp! macro.", fname.to_string_lossy(), err))
+                multi_bindings.get(config).unwrap_or_else(|err| panic!("Unable to find a suitable set of bindings within the JSON archive {} ({}). This likely means that the codegen tool hasn't been rerun since some changes in your include_cpp! macro.", fnames.to_string_lossy(), err))
             }
         }
     }