From 27d447f3b3f43f899424742bd6cb103d7773efcb Mon Sep 17 00:00:00 2001
From: Cameron Bytheway <bytheway.cameron@gmail.com>
Date: Thu, 16 Nov 2023 12:06:17 -0700
Subject: [PATCH] fix(aws-lc-sys): add `links` attribute to Cargo.toml (#277)

* fix(aws-lc-sys): add `links` attribute to Cargo.toml

* fix failing ci tasks

* use toml_edit to read expected links value
---
 Cargo.toml                       |  3 +-
 aws-lc-sys-testing/Cargo.toml    | 15 +++++++
 aws-lc-sys-testing/build.rs      | 26 +++++++++++
 aws-lc-sys-testing/src/main.rs   | 18 ++++++++
 aws-lc-sys-testing/src/testing.c |  7 +++
 aws-lc-sys/Cargo.toml            |  2 +
 aws-lc-sys/builder/main.rs       | 75 ++++++++++++++++++++++----------
 7 files changed, 121 insertions(+), 25 deletions(-)
 create mode 100644 aws-lc-sys-testing/Cargo.toml
 create mode 100644 aws-lc-sys-testing/build.rs
 create mode 100644 aws-lc-sys-testing/src/main.rs
 create mode 100644 aws-lc-sys-testing/src/testing.c

diff --git a/Cargo.toml b/Cargo.toml
index 550a025e343..2eba2c9da11 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,8 @@ members = [
     "aws-lc-rs",
     "aws-lc-sys",
     "aws-lc-fips-sys",
-    "aws-lc-rs-testing"
+    "aws-lc-rs-testing",
+    "aws-lc-sys-testing",
 ]
 resolver = "2"
 
diff --git a/aws-lc-sys-testing/Cargo.toml b/aws-lc-sys-testing/Cargo.toml
new file mode 100644
index 00000000000..6a4abcca7d3
--- /dev/null
+++ b/aws-lc-sys-testing/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "aws-lc-sys-testing"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+aws-lc-sys = { path = "../aws-lc-sys" }
+
+[build-dependencies]
+cc = "1"
+toml_edit = "0.21"
+
+[package.metadata.cargo-udeps.ignore]
+normal = [ "aws-lc-sys" ] # the sys crate is only used through a C library build
diff --git a/aws-lc-sys-testing/build.rs b/aws-lc-sys-testing/build.rs
new file mode 100644
index 00000000000..f8d4eb536c1
--- /dev/null
+++ b/aws-lc-sys-testing/build.rs
@@ -0,0 +1,26 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0 OR ISC
+
+use toml_edit::Document;
+
+fn main() {
+    let cargo_toml = std::fs::read_to_string("../aws-lc-sys/Cargo.toml").unwrap();
+    let cargo_toml = cargo_toml.parse::<Document>().unwrap();
+
+    let links = cargo_toml["package"]["links"].as_str().unwrap();
+
+    // ensure that the include path is exported and set up correctly
+    cc::Build::new()
+        .include(env(format!("DEP_{}_INCLUDE", links.to_uppercase())))
+        .file("src/testing.c")
+        .compile("aws_ls_sys_testing");
+
+    // ensure the libcrypto artifact is linked
+    println!("cargo:rustc-link-lib={}_crypto", links);
+}
+
+fn env<S: AsRef<str>>(s: S) -> String {
+    let s = s.as_ref();
+    println!("cargo:rerun-if-env-changed={s}");
+    std::env::var(s).unwrap_or_else(|_| panic!("missing env var {s}"))
+}
diff --git a/aws-lc-sys-testing/src/main.rs b/aws-lc-sys-testing/src/main.rs
new file mode 100644
index 00000000000..f4ee8e8d25d
--- /dev/null
+++ b/aws-lc-sys-testing/src/main.rs
@@ -0,0 +1,18 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0 OR ISC
+
+use std::ffi::c_int;
+
+extern "C" {
+    fn testing_evp_key_type(nid: c_int) -> c_int;
+}
+
+fn main() {
+    let v = unsafe { testing_evp_key_type(123) };
+    println!("Hello EVP {v}!");
+}
+
+#[test]
+fn link_test() {
+    let _ = unsafe { testing_evp_key_type(123) };
+}
diff --git a/aws-lc-sys-testing/src/testing.c b/aws-lc-sys-testing/src/testing.c
new file mode 100644
index 00000000000..83682acb9c7
--- /dev/null
+++ b/aws-lc-sys-testing/src/testing.c
@@ -0,0 +1,7 @@
+#include <openssl/is_awslc.h>
+#include <openssl/evp.h>
+
+int testing_evp_key_type(int nid)
+{
+    return EVP_PKEY_type(nid);
+}
diff --git a/aws-lc-sys/Cargo.toml b/aws-lc-sys/Cargo.toml
index a38a05262b5..230f3fffd5e 100644
--- a/aws-lc-sys/Cargo.toml
+++ b/aws-lc-sys/Cargo.toml
@@ -2,6 +2,7 @@
 name = "aws-lc-sys"
 description = "AWS-LC is a general-purpose cryptographic library maintained by the AWS Cryptography team for AWS and their customers. It іs based on code from the Google BoringSSL project and the OpenSSL project."
 version = "0.12.0"
+links = "aws_lc_0_12_0"
 authors = ["AWS-LC"]
 edition = "2021"
 repository = "https://github.com/aws/aws-lc-rs"
@@ -51,6 +52,7 @@ bindgen = ["dep:bindgen"] # Generate the bindings on the targetted platform as a
 [build-dependencies]
 cmake = "0.1.48"
 dunce = "1.0"
+fs_extra = "1"
 
 [target.'cfg(any(all(target_os = "macos", target_arch = "x86_64"), all(target_os = "linux", target_arch = "x86"), all(target_os = "linux", target_arch = "x86_64"), all(target_os = "linux", target_arch = "aarch64")))'.build-dependencies]
 bindgen = { version = "0.69.1", optional = true }
diff --git a/aws-lc-sys/builder/main.rs b/aws-lc-sys/builder/main.rs
index 43e929029cc..ddb0e5b1709 100644
--- a/aws-lc-sys/builder/main.rs
+++ b/aws-lc-sys/builder/main.rs
@@ -84,17 +84,16 @@ impl OutputLibType {
 
 impl OutputLib {
     fn libname(self, prefix: Option<&str>) -> String {
-        format!(
-            "{}{}",
-            if let Some(pfix) = prefix { pfix } else { "" },
-            match self {
-                OutputLib::Crypto => "crypto",
-                OutputLib::Ssl => "ssl",
-                OutputLib::RustWrapper => {
-                    "rust_wrapper"
-                }
-            }
-        )
+        let name = match self {
+            OutputLib::Crypto => "crypto",
+            OutputLib::Ssl => "ssl",
+            OutputLib::RustWrapper => "rust_wrapper",
+        };
+        if let Some(prefix) = prefix {
+            format!("{prefix}_{name}")
+        } else {
+            name.to_string()
+        }
     }
 }
 
@@ -200,7 +199,7 @@ fn prepare_cmake_build(manifest_dir: &PathBuf, build_prefix: String) -> cmake::C
 }
 
 fn build_rust_wrapper(manifest_dir: &PathBuf) -> PathBuf {
-    prepare_cmake_build(manifest_dir, prefix_string())
+    prepare_cmake_build(manifest_dir, prefix_string() + "_")
         .configure_arg("--no-warn-unused-cli")
         .build()
 }
@@ -374,18 +373,10 @@ fn main() {
         RustWrapper.libname(Some(&prefix))
     );
 
-    for include_path in [
-        get_rust_include_path(&manifest_dir),
-        get_generated_include_path(&manifest_dir),
-        get_aws_lc_include_path(&manifest_dir),
-    ] {
-        println!("cargo:include={}", include_path.display());
-    }
-    if let Some(include_paths) = get_aws_lc_sys_includes_path() {
-        for path in include_paths {
-            println!("cargo:include={}", path.display());
-        }
-    }
+    println!(
+        "cargo:include={}",
+        setup_include_paths(&out_dir, &manifest_dir).display()
+    );
 
     println!("cargo:rerun-if-changed=builder/");
     println!("cargo:rerun-if-changed=aws-lc/");
@@ -407,3 +398,39 @@ fn check_dependencies() {
         "Required build dependency is missing. Halting build."
     );
 }
+
+fn setup_include_paths(out_dir: &Path, manifest_dir: &Path) -> PathBuf {
+    let mut include_paths = vec![
+        get_rust_include_path(manifest_dir),
+        get_generated_include_path(manifest_dir),
+        get_aws_lc_include_path(manifest_dir),
+    ];
+
+    if let Some(extra_paths) = get_aws_lc_sys_includes_path() {
+        include_paths.extend(extra_paths);
+    }
+
+    let include_dir = out_dir.join("include");
+    std::fs::create_dir_all(&include_dir).unwrap();
+
+    // iterate over all of the include paths and copy them into the final output
+    for path in include_paths {
+        for child in std::fs::read_dir(path).into_iter().flatten().flatten() {
+            if child.file_type().map_or(false, |t| t.is_file()) {
+                let _ = std::fs::copy(
+                    child.path(),
+                    include_dir.join(child.path().file_name().unwrap()),
+                );
+                continue;
+            }
+
+            // prefer the earliest paths
+            let options = fs_extra::dir::CopyOptions::new()
+                .skip_exist(true)
+                .copy_inside(true);
+            let _ = fs_extra::dir::copy(child.path(), &include_dir, &options);
+        }
+    }
+
+    include_dir
+}