diff options
54 files changed, 6256 insertions, 1082 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index d38a96172..e032c4df7 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,6 +1,9 @@ # Per-project `repo upload` hook settings. # https://android.googlesource.com/platform/tools/repohooks +[Builtin Hooks] +rustfmt = true + [Options] ignore_merged_commits = true @@ -11,4 +14,4 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPL ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/ktfmt_includes.txt ${PREUPLOAD_FILES} -ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
\ No newline at end of file +ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES} diff --git a/build/Android.mk b/build/tools/sdk-preprocess-files.mk index b05bcd5ad..b05bcd5ad 100644 --- a/build/Android.mk +++ b/build/tools/sdk-preprocess-files.mk diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java index 25048276f..f821a0e5a 100644 --- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java +++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java @@ -481,18 +481,14 @@ public class Monkey { private void commandLineReport(String reportName, String command) { Logger.err.println(reportName + ":"); Runtime rt = Runtime.getRuntime(); - Writer logOutput = null; - try { + try (Writer logOutput = mRequestBugreport ? + new BufferedWriter(new FileWriter(new File(Environment + .getLegacyExternalStorageDirectory(), reportName), true)) : null) { // Process must be fully qualified here because android.os.Process // is used elsewhere java.lang.Process p = Runtime.getRuntime().exec(command); - if (mRequestBugreport) { - logOutput = - new BufferedWriter(new FileWriter(new File(Environment - .getLegacyExternalStorageDirectory(), reportName), true)); - } // pipe everything from process stdout -> System.err InputStream inStream = p.getInputStream(); InputStreamReader inReader = new InputStreamReader(inStream); @@ -519,10 +515,6 @@ public class Monkey { int status = p.waitFor(); Logger.err.println("// " + reportName + " status was " + status); - - if (logOutput != null) { - logOutput.close(); - } } catch (Exception e) { Logger.err.println("// Exception from " + reportName + ":"); Logger.err.println(e.toString()); diff --git a/gki/Android.mk b/gki/Android.mk deleted file mode 100644 index 98675ffde..000000000 --- a/gki/Android.mk +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright 2021 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -gki_path := $(LOCAL_PATH) - -include $(gki_path)/kmi_abi_chk/kmi_compatibility_test.mk diff --git a/gki/kmi_abi_chk/Android.bp b/gki/kmi_abi_chk/Android.bp new file mode 100644 index 000000000..f1ff6e5bd --- /dev/null +++ b/gki/kmi_abi_chk/Android.bp @@ -0,0 +1,54 @@ +// +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + default_applicable_licenses: [ + "Android-Apache-2.0", + ], +} + +genrule { + name: "a14_5_15_kmi_compatibility_test", + srcs: [ + "sym-5.15/Module.symvers-10342779", + ], + tool_files: ["kmi_compatibility_test.sh"], + out: ["a14_5_15_kmi_compatibility_test"], + // kmi_compatibility_test.sh $(CURR_SYMVERS) $(PREV_SYMVERS) + cmd: "$(location kmi_compatibility_test.sh) $(location sym-5.15/Module.symvers-10342779) $(location sym-5.15/Module.symvers-10342779) && " + + "touch $(out)", +} + +genrule { + name: "a14_6_1_kmi_compatibility_test", + srcs: [ + "sym-6.1/Module.symvers-10816536", + ], + tool_files: ["kmi_compatibility_test.sh"], + out: ["a14_6_1_kmi_compatibility_test"], + // kmi_compatibility_test.sh $(CURR_SYMVERS) $(PREV_SYMVERS) + cmd: "$(location kmi_compatibility_test.sh) $(location sym-6.1/Module.symvers-10816536) $(location sym-6.1/Module.symvers-10816536) && " + + "touch $(out)", +} + +phony_rule { + name: "a14_kmi_compatibility_test", + phony_deps: [ + "a14_5_15_kmi_compatibility_test", + "a14_6_1_kmi_compatibility_test", + ], +} diff --git a/gki/kmi_abi_chk/kmi_compatibility_test.mk b/gki/kmi_abi_chk/kmi_compatibility_test.mk deleted file mode 100644 index 3abc91017..000000000 --- a/gki/kmi_abi_chk/kmi_compatibility_test.mk +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) 2021 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -LOCAL_PATH := $(call my-dir) - -KMI_CHK_SCRIPT := $(LOCAL_PATH)/kmi_compatibility_test.sh - -# Current kernel symbol files to be checked -# Use the one under $(LOCAL_PATH)/sym-[56].* by default for self testing. -# The reason not to use the one under kernel/prebuilts/5.* by default -# is because the KMI ABI may not be stable during development. -# -# Set CURR_5_15_SYMVERS/CURR_6_1_SYMVERS explicitly for the actual -# current kernel symbol file to be checked. E.g., -# $ m CURR_6_1_SYMVERS=kernel/prebuilts/6.1/arm64/Module.symvers \ -# gki_6_1_kmi_compatibility_test -CURR_5_15_SYMVERS ?= development/gki/kmi_abi_chk/sym-5.15/Module.symvers -CURR_6_1_SYMVERS ?= development/gki/kmi_abi_chk/sym-6.1/Module.symvers - -# Previous kernel symbol files, against which the latest one is checked -# The file names of previous kernel symbol files are of this form: -# *.symvers-$(BID) -# Here *.symvers is a symbolic link to the latest build. -PREV_5_15_SYMVERS := $(LOCAL_PATH)/sym-5.15/Module.symvers -PREV_6_1_SYMVERS := $(LOCAL_PATH)/sym-6.1/Module.symvers - -include $(CLEAR_VARS) -LOCAL_MODULE := a14_5_15_kmi_compatibility_test -LOCAL_MODULE_CLASS := FAKE -LOCAL_MODULE_TAGS := optional -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -include $(BUILD_SYSTEM)/base_rules.mk - -$(LOCAL_BUILT_MODULE): $(KMI_CHK_SCRIPT) $(CURR_5_15_SYMVERS) $(PREV_5_15_SYMVERS) - @mkdir -p $(dir $@) - $(hide) $(KMI_CHK_SCRIPT) $(CURR_5_15_SYMVERS) $(PREV_5_15_SYMVERS) - $(hide) touch $@ - -include $(CLEAR_VARS) -LOCAL_MODULE := a14_6_1_kmi_compatibility_test -LOCAL_MODULE_CLASS := FAKE -LOCAL_MODULE_TAGS := optional -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -include $(BUILD_SYSTEM)/base_rules.mk - -$(LOCAL_BUILT_MODULE): $(KMI_CHK_SCRIPT) $(CURR_6_1_SYMVERS) $(PREV_6_1_SYMVERS) - @mkdir -p $(dir $@) - $(hide) $(KMI_CHK_SCRIPT) $(CURR_6_1_SYMVERS) $(PREV_6_1_SYMVERS) - $(hide) touch $@ - -include $(CLEAR_VARS) -LOCAL_MODULE := a14_kmi_compatibility_test -LOCAL_REQUIRED_MODULES := a14_5_15_kmi_compatibility_test a14_6_1_kmi_compatibility_test -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -include $(BUILD_PHONY_PACKAGE) diff --git a/rustfmt.toml b/rustfmt.toml new file mode 120000 index 000000000..239055a55 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +../build/soong/scripts/rustfmt.toml
\ No newline at end of file diff --git a/samples/training/NsdChat/Android.bp b/samples/training/NsdChat/Android.bp deleted file mode 100644 index dc1f127af..000000000 --- a/samples/training/NsdChat/Android.bp +++ /dev/null @@ -1,13 +0,0 @@ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -android_app { - name: "NsdChat", - // Only compile source java files in this apk. - srcs: ["src/**/*.java"], - sdk_version: "current", - optimize: { - proguard_flags_files: ["proguard.cfg"], - }, -} diff --git a/samples/training/NsdChat/AndroidManifest.xml b/samples/training/NsdChat/AndroidManifest.xml deleted file mode 100644 index bb5e73c7c..000000000 --- a/samples/training/NsdChat/AndroidManifest.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.android.nsdchat" - android:versionCode="1" - android:versionName="1.0" > - - <uses-sdk android:minSdkVersion="16" - android:targetSdkVersion="16" /> - <uses-permission android:required="true" android:name="android.permission.INTERNET"/> - - <application - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" > - <activity - android:name="com.example.android.nsdchat.NsdChatActivity" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> - -</manifest> diff --git a/samples/training/NsdChat/proguard.cfg b/samples/training/NsdChat/proguard.cfg deleted file mode 100644 index 52f242d8a..000000000 --- a/samples/training/NsdChat/proguard.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Keep onClickListeners. --keepclassmembers class * extends android.app.Activity { - public void *(android.view.View); -} - diff --git a/samples/training/NsdChat/project.properties b/samples/training/NsdChat/project.properties deleted file mode 100644 index 9b84a6b4b..000000000 --- a/samples/training/NsdChat/project.properties +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-16 diff --git a/samples/training/NsdChat/res/drawable-hdpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 96a442e5b..000000000 --- a/samples/training/NsdChat/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/samples/training/NsdChat/res/drawable-ldpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-ldpi/ic_launcher.png Binary files differdeleted file mode 100644 index 99238729d..000000000 --- a/samples/training/NsdChat/res/drawable-ldpi/ic_launcher.png +++ /dev/null diff --git a/samples/training/NsdChat/res/drawable-mdpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 359047dfa..000000000 --- a/samples/training/NsdChat/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/samples/training/NsdChat/res/drawable-xhdpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 71c6d760f..000000000 --- a/samples/training/NsdChat/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/training/NsdChat/res/layout/main.xml b/samples/training/NsdChat/res/layout/main.xml deleted file mode 100644 index e516458f1..000000000 --- a/samples/training/NsdChat/res/layout/main.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:orientation="vertical" > - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="96dp" - android:orientation="horizontal" > - - <Button - android:id="@+id/advertise_btn" - android:layout_width="96dp" - android:layout_height="64dp" - android:onClick="clickAdvertise" - android:text="@string/register" /> - - <Button - android:id="@+id/discover_btn" - android:layout_width="96dp" - android:layout_height="64dp" - android:onClick="clickDiscover" - android:text="@string/discover" /> - - <Button - android:id="@+id/connect_btn" - android:layout_width="96dp" - android:layout_height="64dp" - android:onClick="clickConnect" - android:text="@string/connect" /> - </LinearLayout> - - <TextView - android:id="@+id/status" - android:layout_width="fill_parent" - android:layout_height="200dp" - android:focusable="true" /> - - <EditText - android:id="@+id/chatInput" - android:layout_width="fill_parent" - android:layout_height="80dp" - android:inputType="text" - android:singleLine="true" /> - - <Button - android:id="@+id/send_btn" - android:layout_width="96dp" - android:layout_height="64dp" - android:onClick="clickSend" - android:text="@string/send" /> - -</LinearLayout>
\ No newline at end of file diff --git a/samples/training/NsdChat/res/values/strings.xml b/samples/training/NsdChat/res/values/strings.xml deleted file mode 100644 index 1e15e362d..000000000 --- a/samples/training/NsdChat/res/values/strings.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - - <string name="app_name">NsdChat</string> - - <string name="connect">Connect</string> - <string name="discover">Discover</string> - <string name="register">Register</string> - <string name="send">Send</string> - -</resources> diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java b/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java deleted file mode 100644 index 534f218bd..000000000 --- a/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.android.nsdchat; - -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; - -public class ChatConnection { - - private Handler mUpdateHandler; - private ChatServer mChatServer; - private ChatClient mChatClient; - - private static final String TAG = "ChatConnection"; - - private Socket mSocket; - private int mPort = -1; - - public ChatConnection(Handler handler) { - mUpdateHandler = handler; - mChatServer = new ChatServer(handler); - } - - public void tearDown() { - mChatServer.tearDown(); - if (mChatClient != null) { - mChatClient.tearDown(); - } - } - - public void connectToServer(InetAddress address, int port) { - mChatClient = new ChatClient(address, port); - } - - public void sendMessage(String msg) { - if (mChatClient != null) { - mChatClient.sendMessage(msg); - } - } - - public int getLocalPort() { - return mPort; - } - - public void setLocalPort(int port) { - mPort = port; - } - - - public synchronized void updateMessages(String msg, boolean local) { - Log.e(TAG, "Updating message: " + msg); - - if (local) { - msg = "me: " + msg; - } else { - msg = "them: " + msg; - } - - Bundle messageBundle = new Bundle(); - messageBundle.putString("msg", msg); - - Message message = new Message(); - message.setData(messageBundle); - mUpdateHandler.sendMessage(message); - - } - - private synchronized void setSocket(Socket socket) { - Log.d(TAG, "setSocket being called."); - if (socket == null) { - Log.d(TAG, "Setting a null socket."); - } - if (mSocket != null) { - if (mSocket.isConnected()) { - try { - mSocket.close(); - } catch (IOException e) { - // TODO(alexlucas): Auto-generated catch block - e.printStackTrace(); - } - } - } - mSocket = socket; - } - - private Socket getSocket() { - return mSocket; - } - - private class ChatServer { - ServerSocket mServerSocket = null; - Thread mThread = null; - - public ChatServer(Handler handler) { - mThread = new Thread(new ServerThread()); - mThread.start(); - } - - public void tearDown() { - mThread.interrupt(); - try { - mServerSocket.close(); - } catch (IOException ioe) { - Log.e(TAG, "Error when closing server socket."); - } - } - - class ServerThread implements Runnable { - - @Override - public void run() { - - try { - // Since discovery will happen via Nsd, we don't need to care which port is - // used. Just grab an available one and advertise it via Nsd. - mServerSocket = new ServerSocket(0); - setLocalPort(mServerSocket.getLocalPort()); - - while (!Thread.currentThread().isInterrupted()) { - Log.d(TAG, "ServerSocket Created, awaiting connection"); - setSocket(mServerSocket.accept()); - Log.d(TAG, "Connected."); - if (mChatClient == null) { - int port = mSocket.getPort(); - InetAddress address = mSocket.getInetAddress(); - connectToServer(address, port); - } - } - } catch (IOException e) { - Log.e(TAG, "Error creating ServerSocket: ", e); - e.printStackTrace(); - } - } - } - } - - private class ChatClient { - - private InetAddress mAddress; - private int PORT; - - private final String CLIENT_TAG = "ChatClient"; - - private Thread mSendThread; - private Thread mRecThread; - - public ChatClient(InetAddress address, int port) { - - Log.d(CLIENT_TAG, "Creating chatClient"); - this.mAddress = address; - this.PORT = port; - - mSendThread = new Thread(new SendingThread()); - mSendThread.start(); - } - - class SendingThread implements Runnable { - - BlockingQueue<String> mMessageQueue; - private int QUEUE_CAPACITY = 10; - - public SendingThread() { - mMessageQueue = new ArrayBlockingQueue<String>(QUEUE_CAPACITY); - } - - @Override - public void run() { - try { - if (getSocket() == null) { - setSocket(new Socket(mAddress, PORT)); - Log.d(CLIENT_TAG, "Client-side socket initialized."); - - } else { - Log.d(CLIENT_TAG, "Socket already initialized. skipping!"); - } - - mRecThread = new Thread(new ReceivingThread()); - mRecThread.start(); - - } catch (UnknownHostException e) { - Log.d(CLIENT_TAG, "Initializing socket failed, UHE", e); - } catch (IOException e) { - Log.d(CLIENT_TAG, "Initializing socket failed, IOE.", e); - } - - while (true) { - try { - String msg = mMessageQueue.take(); - sendMessage(msg); - } catch (InterruptedException ie) { - Log.d(CLIENT_TAG, "Message sending loop interrupted, exiting"); - } - } - } - } - - class ReceivingThread implements Runnable { - - @Override - public void run() { - - BufferedReader input; - try { - input = new BufferedReader(new InputStreamReader( - mSocket.getInputStream())); - while (!Thread.currentThread().isInterrupted()) { - - String messageStr = null; - messageStr = input.readLine(); - if (messageStr != null) { - Log.d(CLIENT_TAG, "Read from the stream: " + messageStr); - updateMessages(messageStr, false); - } else { - Log.d(CLIENT_TAG, "The nulls! The nulls!"); - break; - } - } - input.close(); - - } catch (IOException e) { - Log.e(CLIENT_TAG, "Server loop error: ", e); - } - } - } - - public void tearDown() { - try { - getSocket().close(); - } catch (IOException ioe) { - Log.e(CLIENT_TAG, "Error when closing server socket."); - } - } - - public void sendMessage(String msg) { - try { - Socket socket = getSocket(); - if (socket == null) { - Log.d(CLIENT_TAG, "Socket is null, wtf?"); - } else if (socket.getOutputStream() == null) { - Log.d(CLIENT_TAG, "Socket output stream is null, wtf?"); - } - - PrintWriter out = new PrintWriter( - new BufferedWriter( - new OutputStreamWriter(getSocket().getOutputStream())), true); - out.println(msg); - out.flush(); - updateMessages(msg, true); - } catch (UnknownHostException e) { - Log.d(CLIENT_TAG, "Unknown Host", e); - } catch (IOException e) { - Log.d(CLIENT_TAG, "I/O Exception", e); - } catch (Exception e) { - Log.d(CLIENT_TAG, "Error3", e); - } - Log.d(CLIENT_TAG, "Client sent message: " + msg); - } - } -} diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java deleted file mode 100644 index 5782634ff..000000000 --- a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.android.nsdchat; - -import android.app.Activity; -import android.net.nsd.NsdServiceInfo; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.util.Log; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; - -import com.example.android.nsdchat.NsdHelper; - -public class NsdChatActivity extends Activity { - - NsdHelper mNsdHelper; - - private TextView mStatusView; - private Handler mUpdateHandler; - - public static final String TAG = "NsdChat"; - - ChatConnection mConnection; - - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.d(TAG, "Creating chat activity"); - setContentView(R.layout.main); - mStatusView = (TextView) findViewById(R.id.status); - - mUpdateHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - String chatLine = msg.getData().getString("msg"); - addChatLine(chatLine); - } - }; - - } - - public void clickAdvertise(View v) { - // Register service - if(mConnection.getLocalPort() > -1) { - mNsdHelper.registerService(mConnection.getLocalPort()); - } else { - Log.d(TAG, "ServerSocket isn't bound."); - } - } - - public void clickDiscover(View v) { - mNsdHelper.discoverServices(); - } - - public void clickConnect(View v) { - NsdServiceInfo service = mNsdHelper.getChosenServiceInfo(); - if (service != null) { - Log.d(TAG, "Connecting."); - mConnection.connectToServer(service.getHost(), - service.getPort()); - } else { - Log.d(TAG, "No service to connect to!"); - } - } - - public void clickSend(View v) { - EditText messageView = (EditText) this.findViewById(R.id.chatInput); - if (messageView != null) { - String messageString = messageView.getText().toString(); - if (!messageString.isEmpty()) { - mConnection.sendMessage(messageString); - } - messageView.setText(""); - } - } - - public void addChatLine(String line) { - mStatusView.append("\n" + line); - } - - @Override - protected void onStart() { - Log.d(TAG, "Starting."); - mConnection = new ChatConnection(mUpdateHandler); - - mNsdHelper = new NsdHelper(this); - mNsdHelper.initializeNsd(); - super.onStart(); - } - - - @Override - protected void onPause() { - Log.d(TAG, "Pausing."); - if (mNsdHelper != null) { - mNsdHelper.stopDiscovery(); - } - super.onPause(); - } - - @Override - protected void onResume() { - Log.d(TAG, "Resuming."); - super.onResume(); - if (mNsdHelper != null) { - mNsdHelper.discoverServices(); - } - } - - - // For KitKat and earlier releases, it is necessary to remove the - // service registration when the application is stopped. There's - // no guarantee that the onDestroy() method will be called (we're - // killable after onStop() returns) and the NSD service won't remove - // the registration for us if we're killed. - - // In L and later, NsdService will automatically unregister us when - // our connection goes away when we're killed, so this step is - // optional (but recommended). - - @Override - protected void onStop() { - Log.d(TAG, "Being stopped."); - mNsdHelper.tearDown(); - mConnection.tearDown(); - mNsdHelper = null; - mConnection = null; - super.onStop(); - } - - @Override - protected void onDestroy() { - Log.d(TAG, "Being destroyed."); - super.onDestroy(); - } -} diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java deleted file mode 100644 index 5111318cf..000000000 --- a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.android.nsdchat; - -import android.content.Context; -import android.net.nsd.NsdServiceInfo; -import android.net.nsd.NsdManager; -import android.util.Log; - -public class NsdHelper { - - Context mContext; - - NsdManager mNsdManager; - NsdManager.ResolveListener mResolveListener; - NsdManager.DiscoveryListener mDiscoveryListener; - NsdManager.RegistrationListener mRegistrationListener; - - public static final String SERVICE_TYPE = "_http._tcp."; - - public static final String TAG = "NsdHelper"; - public String mServiceName = "NsdChat"; - - NsdServiceInfo mService; - - public NsdHelper(Context context) { - mContext = context; - mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); - } - - public void initializeNsd() { - initializeResolveListener(); - - //mNsdManager.init(mContext.getMainLooper(), this); - - } - - public void initializeDiscoveryListener() { - mDiscoveryListener = new NsdManager.DiscoveryListener() { - - @Override - public void onDiscoveryStarted(String regType) { - Log.d(TAG, "Service discovery started"); - } - - @Override - public void onServiceFound(NsdServiceInfo service) { - Log.d(TAG, "Service discovery success" + service); - if (!service.getServiceType().equals(SERVICE_TYPE)) { - Log.d(TAG, "Unknown Service Type: " + service.getServiceType()); - } else if (service.getServiceName().equals(mServiceName)) { - Log.d(TAG, "Same machine: " + mServiceName); - } else if (service.getServiceName().contains(mServiceName)){ - mNsdManager.resolveService(service, mResolveListener); - } - } - - @Override - public void onServiceLost(NsdServiceInfo service) { - Log.e(TAG, "service lost" + service); - if (mService == service) { - mService = null; - } - } - - @Override - public void onDiscoveryStopped(String serviceType) { - Log.i(TAG, "Discovery stopped: " + serviceType); - } - - @Override - public void onStartDiscoveryFailed(String serviceType, int errorCode) { - Log.e(TAG, "Discovery failed: Error code:" + errorCode); - } - - @Override - public void onStopDiscoveryFailed(String serviceType, int errorCode) { - Log.e(TAG, "Discovery failed: Error code:" + errorCode); - } - }; - } - - public void initializeResolveListener() { - mResolveListener = new NsdManager.ResolveListener() { - - @Override - public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { - Log.e(TAG, "Resolve failed" + errorCode); - } - - @Override - public void onServiceResolved(NsdServiceInfo serviceInfo) { - Log.e(TAG, "Resolve Succeeded. " + serviceInfo); - - if (serviceInfo.getServiceName().equals(mServiceName)) { - Log.d(TAG, "Same IP."); - return; - } - mService = serviceInfo; - } - }; - } - - public void initializeRegistrationListener() { - mRegistrationListener = new NsdManager.RegistrationListener() { - - @Override - public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) { - mServiceName = NsdServiceInfo.getServiceName(); - Log.d(TAG, "Service registered: " + mServiceName); - } - - @Override - public void onRegistrationFailed(NsdServiceInfo arg0, int arg1) { - Log.d(TAG, "Service registration failed: " + arg1); - } - - @Override - public void onServiceUnregistered(NsdServiceInfo arg0) { - Log.d(TAG, "Service unregistered: " + arg0.getServiceName()); - } - - @Override - public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { - Log.d(TAG, "Service unregistration failed: " + errorCode); - } - - }; - } - - public void registerService(int port) { - tearDown(); // Cancel any previous registration request - initializeRegistrationListener(); - NsdServiceInfo serviceInfo = new NsdServiceInfo(); - serviceInfo.setPort(port); - serviceInfo.setServiceName(mServiceName); - serviceInfo.setServiceType(SERVICE_TYPE); - - mNsdManager.registerService( - serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); - - } - - public void discoverServices() { - stopDiscovery(); // Cancel any existing discovery request - initializeDiscoveryListener(); - mNsdManager.discoverServices( - SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); - } - - public void stopDiscovery() { - if (mDiscoveryListener != null) { - try { - mNsdManager.stopServiceDiscovery(mDiscoveryListener); - } finally { - } - mDiscoveryListener = null; - } - } - - public NsdServiceInfo getChosenServiceInfo() { - return mService; - } - - public void tearDown() { - if (mRegistrationListener != null) { - try { - mNsdManager.unregisterService(mRegistrationListener); - } finally { - } - mRegistrationListener = null; - } - } -} diff --git a/scripts/stack_core.py b/scripts/stack_core.py index 67ae536bd..215a45d5a 100755 --- a/scripts/stack_core.py +++ b/scripts/stack_core.py @@ -62,14 +62,6 @@ class TraceConverter: apk_info = dict() lib_to_path = dict() - register_names = { - "arm": "r0|r1|r2|r3|r4|r5|r6|r7|r8|r9|sl|fp|ip|sp|lr|pc|cpsr", - "arm64": "x0|x1|x2|x3|x4|x5|x6|x7|x8|x9|x10|x11|x12|x13|x14|x15|x16|x17|x18|x19|x20|x21|x22|x23|x24|x25|x26|x27|x28|x29|x30|sp|pc|pstate", - "x86": "eax|ebx|ecx|edx|esi|edi|x?cs|x?ds|x?es|x?fs|x?ss|eip|ebp|esp|flags", - "x86_64": "rax|rbx|rcx|rdx|rsi|rdi|r8|r9|r10|r11|r12|r13|r14|r15|cs|ss|rip|rbp|rsp|eflags", - "riscv64": "ra|sp|gp|tp|t0|t1|t2|s0|s1|a0|a1|a2|a3|a4|a5|a6|a7|s2|s3|s4|s5|s6|s7|s8|s9|s10|s11|t3|t4|t5|t6|pc", - } - # We use the "file" command line tool to extract BuildId from ELF files. ElfInfo = collections.namedtuple("ElfInfo", ["bitness", "build_id"]) readelf_output = re.compile(r"Class:\s*ELF(?P<bitness>32|64).*" diff --git a/scripts/symbol.py b/scripts/symbol.py index f4c239535..64242eab8 100755 --- a/scripts/symbol.py +++ b/scripts/symbol.py @@ -20,6 +20,7 @@ The information can include symbol names, offsets, and source locations. """ import atexit +import json import glob import os import platform @@ -292,7 +293,7 @@ def CallLlvmSymbolizerForSet(lib, unique_addrs): return None cmd = [ToolPath("llvm-symbolizer"), "--functions", "--inlines", - "--demangle", "--obj=" + symbols, "--output-style=GNU"] + "--demangle", "--obj=" + symbols, "--output-style=JSON"] child = _PIPE_ADDR2LINE_CACHE.GetProcess(cmd) for addr in addrs: @@ -300,20 +301,12 @@ def CallLlvmSymbolizerForSet(lib, unique_addrs): child.stdin.write("0x%s\n" % addr) child.stdin.flush() records = [] - first = True - while True: - symbol = child.stdout.readline().strip() - if not symbol: - break - location = child.stdout.readline().strip() - records.append((symbol, location)) - if first: - # Write a blank line as a sentinel so we know when to stop - # reading inlines from the output. - # The blank line will cause llvm-symbolizer to emit a blank line. - child.stdin.write("\n") - child.stdin.flush() - first = False + json_result = json.loads(child.stdout.readline().strip()) + for symbol in json_result["Symbol"]: + function_name = symbol["FunctionName"] + # GNU style location: file_name:line_num + location = ("%s:%s" % (symbol["FileName"], symbol["Line"])) + records.append((function_name, location)) except IOError as e: # Remove the / in front of the library name to match other output. records = [(None, lib[1:] + " ***Error: " + str(e))] diff --git a/tools/cargo_embargo/Android.bp b/tools/cargo_embargo/Android.bp index 3ef899193..b1460e52c 100644 --- a/tools/cargo_embargo/Android.bp +++ b/tools/cargo_embargo/Android.bp @@ -33,6 +33,7 @@ rust_defaults { "libregex", "libserde", "libserde_json", + "libtempfile", ], } diff --git a/tools/cargo_embargo/src/cargo/metadata.rs b/tools/cargo_embargo/src/cargo/metadata.rs index 127c81630..ea4c95983 100644 --- a/tools/cargo_embargo/src/cargo/metadata.rs +++ b/tools/cargo_embargo/src/cargo/metadata.rs @@ -71,7 +71,8 @@ impl DependencyMetadata { } } } - !self.optional || features.contains(&format!("dep:{}", self.name)) + let name = self.rename.as_ref().unwrap_or(&self.name); + !self.optional || features.contains(&format!("dep:{}", name)) } } @@ -582,6 +583,88 @@ mod tests { } #[test] + fn get_externs_rename() { + let package = PackageMetadata { + name: "test_package".to_string(), + dependencies: vec![ + DependencyMetadata { + name: "foo".to_string(), + kind: None, + optional: false, + target: None, + rename: Some("foo2".to_string()), + }, + DependencyMetadata { + name: "bar".to_string(), + kind: None, + optional: true, + target: None, + rename: None, + }, + DependencyMetadata { + name: "bar".to_string(), + kind: None, + optional: true, + target: None, + rename: Some("baz".to_string()), + }, + ], + ..Default::default() + }; + let packages = vec![ + package.clone(), + PackageMetadata { + name: "foo".to_string(), + targets: vec![TargetMetadata { + name: "foo".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + PackageMetadata { + name: "bar".to_string(), + targets: vec![TargetMetadata { + name: "bar".to_string(), + kind: vec![TargetKind::Lib], + ..Default::default() + }], + ..Default::default() + }, + ]; + assert_eq!( + get_externs(&package, &packages, &["dep:bar".to_string()], &[], &[], false).unwrap(), + vec![ + Extern { + name: "bar".to_string(), + lib_name: "bar".to_string(), + extern_type: ExternType::Rust + }, + Extern { + name: "foo2".to_string(), + lib_name: "foo".to_string(), + extern_type: ExternType::Rust + }, + ] + ); + assert_eq!( + get_externs(&package, &packages, &["dep:baz".to_string()], &[], &[], false).unwrap(), + vec![ + Extern { + name: "baz".to_string(), + lib_name: "bar".to_string(), + extern_type: ExternType::Rust + }, + Extern { + name: "foo2".to_string(), + lib_name: "foo".to_string(), + extern_type: ExternType::Rust + }, + ] + ); + } + + #[test] fn parse_metadata() { /// Remove anything before "external/rust/crates/" from the /// `package_dir` field. This makes the test robust since you diff --git a/tools/cargo_embargo/src/main.rs b/tools/cargo_embargo/src/main.rs index 753f059e1..bcb7a0810 100644 --- a/tools/cargo_embargo/src/main.rs +++ b/tools/cargo_embargo/src/main.rs @@ -56,6 +56,7 @@ use std::io::{Read, Write}; use std::path::Path; use std::path::PathBuf; use std::process::{Command, Stdio}; +use tempfile::tempdir; // Major TODOs // * handle errors, esp. in cargo.out parsing. they should fail the program with an error code @@ -126,8 +127,11 @@ struct Args { /// Use the cargo binary in the `cargo_bin` directory. Defaults to using the Android prebuilt. #[clap(long)] cargo_bin: Option<PathBuf>, + /// Store `cargo build` output in this directory. If not set, a temporary directory is created and used. + #[clap(long)] + cargo_out_dir: Option<PathBuf>, /// Skip the `cargo build` commands and reuse the "cargo.out" file from a previous run if - /// available. + /// available. Requires setting --cargo_out_dir. #[clap(long)] reuse_cargo_out: bool, #[command(subcommand)] @@ -161,15 +165,21 @@ fn main() -> Result<()> { env_logger::init(); let args = Args::parse(); + if args.reuse_cargo_out && args.cargo_out_dir.is_none() { + return Err(anyhow!("Must specify --cargo_out_dir with --reuse_cargo_out")); + } + let tempdir = tempdir()?; + let intermediates_dir = args.cargo_out_dir.as_deref().unwrap_or(tempdir.path()); + match &args.mode { Mode::DumpCrates { config, crates } => { - dump_crates(&args, config, crates)?; + dump_crates(&args, config, crates, intermediates_dir)?; } Mode::Generate { config } => { - run_embargo(&args, config)?; + run_embargo(&args, config, intermediates_dir)?; } Mode::Autoconfig { config } => { - autoconfig(&args, config)?; + autoconfig(&args, config, intermediates_dir)?; } } @@ -178,9 +188,14 @@ fn main() -> Result<()> { /// Runs cargo_embargo with the given JSON configuration string, but dumps the crate data to the /// given `crates.json` file rather than generating an `Android.bp`. -fn dump_crates(args: &Args, config_filename: &Path, crates_filename: &Path) -> Result<()> { +fn dump_crates( + args: &Args, + config_filename: &Path, + crates_filename: &Path, + intermediates_dir: &Path, +) -> Result<()> { let cfg = Config::from_file(config_filename)?; - let crates = make_all_crates(args, &cfg)?; + let crates = make_all_crates(args, &cfg, intermediates_dir)?; serde_json::to_writer( File::create(crates_filename) .with_context(|| format!("Failed to create {:?}", crates_filename))?, @@ -191,13 +206,13 @@ fn dump_crates(args: &Args, config_filename: &Path, crates_filename: &Path) -> R /// Tries to automatically generate a suitable `cargo_embargo.json` for the package in the current /// directory. -fn autoconfig(args: &Args, config_filename: &Path) -> Result<()> { +fn autoconfig(args: &Args, config_filename: &Path, intermediates_dir: &Path) -> Result<()> { println!("Trying default config with tests..."); let mut config_with_build = Config { variants: vec![VariantConfig { tests: true, ..Default::default() }], package: Default::default(), }; - let mut crates_with_build = make_all_crates(args, &config_with_build)?; + let mut crates_with_build = make_all_crates(args, &config_with_build, intermediates_dir)?; let has_tests = crates_with_build[0].iter().any(|c| c.types.contains(&CrateType::Test) && !c.empty_test); @@ -205,7 +220,7 @@ fn autoconfig(args: &Args, config_filename: &Path) -> Result<()> { println!("No tests, removing from config."); config_with_build = Config { variants: vec![Default::default()], package: Default::default() }; - crates_with_build = make_all_crates(args, &config_with_build)?; + crates_with_build = make_all_crates(args, &config_with_build, intermediates_dir)?; } println!("Trying without cargo build..."); @@ -213,7 +228,7 @@ fn autoconfig(args: &Args, config_filename: &Path) -> Result<()> { variants: vec![VariantConfig { run_cargo: false, tests: has_tests, ..Default::default() }], package: Default::default(), }; - let crates_without_build = make_all_crates(args, &config_no_build)?; + let crates_without_build = make_all_crates(args, &config_no_build, intermediates_dir)?; let config = if crates_with_build == crates_without_build { println!("Output without build was the same, using that."); @@ -266,11 +281,11 @@ fn add_to_path(extra_path: PathBuf) -> Result<()> { } /// Calls make_crates for each variant in the given config. -fn make_all_crates(args: &Args, cfg: &Config) -> Result<Vec<Vec<Crate>>> { - cfg.variants.iter().map(|variant| make_crates(args, variant)).collect() +fn make_all_crates(args: &Args, cfg: &Config, intermediates_dir: &Path) -> Result<Vec<Vec<Crate>>> { + cfg.variants.iter().map(|variant| make_crates(args, variant, intermediates_dir)).collect() } -fn make_crates(args: &Args, cfg: &VariantConfig) -> Result<Vec<Crate>> { +fn make_crates(args: &Args, cfg: &VariantConfig, intermediates_dir: &Path) -> Result<Vec<Crate>> { if !Path::new("Cargo.toml").try_exists().context("when checking Cargo.toml")? { bail!("Cargo.toml missing. Run in a directory with a Cargo.toml file."); } @@ -287,15 +302,16 @@ fn make_crates(args: &Args, cfg: &VariantConfig) -> Result<Vec<Crate>> { }; add_to_path(cargo_bin)?; - let cargo_out_path = "cargo.out"; - let cargo_metadata_path = "cargo.metadata"; - let cargo_output = if args.reuse_cargo_out && Path::new(cargo_out_path).exists() { + let cargo_out_path = intermediates_dir.join("cargo.out"); + let cargo_metadata_path = intermediates_dir.join("cargo.metadata"); + let cargo_output = if args.reuse_cargo_out && cargo_out_path.exists() { CargoOutput { cargo_out: read_to_string(cargo_out_path)?, cargo_metadata: read_to_string(cargo_metadata_path)?, } } else { - let cargo_output = generate_cargo_out(cfg).context("generate_cargo_out failed")?; + let cargo_output = + generate_cargo_out(cfg, intermediates_dir).context("generate_cargo_out failed")?; if cfg.run_cargo { write(cargo_out_path, &cargo_output.cargo_out)?; } @@ -311,9 +327,15 @@ fn make_crates(args: &Args, cfg: &VariantConfig) -> Result<Vec<Crate>> { } /// Runs cargo_embargo with the given JSON configuration file. -fn run_embargo(args: &Args, config_filename: &Path) -> Result<()> { +fn run_embargo(args: &Args, config_filename: &Path, intermediates_dir: &Path) -> Result<()> { + let intermediates_glob = intermediates_dir + .to_str() + .ok_or(anyhow!("Failed to convert intermediate dir path to string"))? + .to_string() + + "target.tmp/**/build/*/out/*"; + let cfg = Config::from_file(config_filename)?; - let crates = make_all_crates(args, &cfg)?; + let crates = make_all_crates(args, &cfg, intermediates_dir)?; // TODO: Use different directories for different variants. // Find out files. @@ -322,7 +344,7 @@ fn run_embargo(args: &Args, config_filename: &Path) -> Result<()> { let mut package_out_files: BTreeMap<String, Vec<Vec<PathBuf>>> = BTreeMap::new(); for (variant_index, variant_cfg) in cfg.variants.iter().enumerate() { if variant_cfg.package.iter().any(|(_, v)| v.copy_out) { - for entry in glob::glob("target.tmp/**/build/*/out/*")? { + for entry in glob::glob(&intermediates_glob)? { match entry { Ok(path) => { let package_name = || -> Option<_> { @@ -347,7 +369,7 @@ fn run_embargo(args: &Args, config_filename: &Path) -> Result<()> { for variant in &mut cfg_no_cargo.variants { variant.run_cargo = false; } - let crates_no_cargo = make_all_crates(args, &cfg_no_cargo)?; + let crates_no_cargo = make_all_crates(args, &cfg_no_cargo, intermediates_dir)?; if crates_no_cargo == crates { eprintln!("Running cargo appears to be unnecessary for this crate, consider adding `\"run_cargo\": false` to your cargo_embargo.json."); } @@ -384,16 +406,24 @@ fn write_all_build_files( let num_variants = cfg.variants.len(); let empty_package_out_files = vec![vec![]; num_variants]; + let mut has_error = false; // Write a build file per package. for (package_dir, crates) in module_by_package { let package_name = &crates.iter().flatten().next().unwrap().package_name; - write_build_files( + if let Err(e) = write_build_files( cfg, package_name, package_dir, &crates, package_out_files.get(package_name).unwrap_or(&empty_package_out_files), - )?; + ) { + // print the error, but continue to accumulate all of the errors + eprintln!("ERROR: {:#}", e); + has_error = true; + } + } + if has_error { + panic!("Encountered fatal errors that must be fixed."); } Ok(()) @@ -434,12 +464,12 @@ pub struct CargoOutput { } /// Run various cargo commands and returns the output. -fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { +fn generate_cargo_out(cfg: &VariantConfig, intermediates_dir: &Path) -> Result<CargoOutput> { let verbose_args = ["-v"]; - let target_dir_args = ["--target-dir", "target.tmp"]; + let target_dir = intermediates_dir.join("target.tmp"); // cargo clean - run_cargo(Command::new("cargo").arg("clean").args(target_dir_args)) + run_cargo(Command::new("cargo").arg("clean").arg("--target-dir").arg(&target_dir)) .context("Running cargo clean")?; let default_target = "x86_64-unknown-linux-gnu"; @@ -498,7 +528,8 @@ fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { .envs(envs.clone()) .args(["build", "--target", default_target]) .args(verbose_args) - .args(target_dir_args) + .arg("--target-dir") + .arg(&target_dir) .args(&workspace_args) .args(&feature_args), )?; @@ -510,7 +541,8 @@ fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { .envs(envs.clone()) .args(["build", "--target", default_target, "--tests"]) .args(verbose_args) - .args(target_dir_args) + .arg("--target-dir") + .arg(&target_dir) .args(&workspace_args) .args(&feature_args), )?; @@ -519,7 +551,8 @@ fn generate_cargo_out(cfg: &VariantConfig) -> Result<CargoOutput> { Command::new("cargo") .envs(envs) .args(["test", "--target", default_target]) - .args(target_dir_args) + .arg("--target-dir") + .arg(&target_dir) .args(&workspace_args) .args(&feature_args) .args(["--", "--list"]), @@ -764,7 +797,8 @@ fn apply_patch_file(output_path: &Path, patch_path: &Path) -> Result<()> { .output() .context("Running patch")?; if !patch_output.status.success() { - eprintln!("WARNING: failed to apply patch {:?}", patch_path); + // These errors will cause the cargo_embargo command to fail, but not yet! + return Err(anyhow!("failed to apply patch {patch_path:?}")); } Ok(()) } diff --git a/tools/external_crates/.gitignore b/tools/external_crates/.gitignore new file mode 100644 index 000000000..2f7896d1d --- /dev/null +++ b/tools/external_crates/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/tools/external_crates/Cargo.lock b/tools/external_crates/Cargo.lock new file mode 100644 index 000000000..a978d8902 --- /dev/null +++ b/tools/external_crates/Cargo.lock @@ -0,0 +1,3035 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "btoi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" +dependencies = [ + "num-traits", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" + +[[package]] +name = "cargo" +version = "0.73.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a6fe1f5394d14b81d2f3f605832a3ce35ed0bf120bc7ef437ce27fd4929c6a" +dependencies = [ + "anyhow", + "base64", + "bytesize", + "cargo-platform", + "cargo-util", + "clap", + "crates-io", + "curl", + "curl-sys", + "env_logger", + "filetime", + "flate2", + "fwdansi", + "git2", + "git2-curl", + "gix", + "gix-features", + "glob", + "hex", + "hmac", + "home", + "http-auth", + "humantime", + "ignore", + "im-rc", + "indexmap 1.9.3", + "itertools 0.10.5", + "jobserver", + "lazycell", + "libc", + "libgit2-sys", + "log", + "memchr", + "opener", + "os_info", + "pasetors", + "pathdiff", + "pulldown-cmark", + "rand", + "rustfix", + "semver", + "serde", + "serde-value", + "serde_ignored", + "serde_json", + "sha1", + "shell-escape", + "strip-ansi-escapes", + "syn", + "tar", + "tempfile", + "termcolor", + "time", + "toml", + "toml_edit", + "unicode-width", + "unicode-xid", + "url", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-util" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2d9a9a8d3e0b61b1110c49ab8f6ed7a76ce4f2b1d53ae48a83152d3d5e8f5b" +dependencies = [ + "anyhow", + "core-foundation", + "filetime", + "hex", + "ignore", + "jobserver", + "libc", + "miow", + "same-file", + "sha2", + "shell-escape", + "tempfile", + "tracing", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "cc" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "clru" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crate_health" +version = "0.1.0" +dependencies = [ + "anyhow", + "cargo", + "clap", + "crate_health_proc_macros", + "glob", + "itertools 0.11.0", + "num_cpus", + "semver", + "serde", + "serde_json", + "tempfile", + "thiserror", + "threadpool", + "tinytemplate", + "walkdir", + "whoami", +] + +[[package]] +name = "crate_health_proc_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "crates-io" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876aa69b4afca5f2eb5e23daa3445930faf829bcb67075a20ffa884f11f8c57c" +dependencies = [ + "anyhow", + "curl", + "percent-encoding", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ct-codecs" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" + +[[package]] +name = "curl" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.72+curl-8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "windows-sys 0.52.0", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519-compact" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" +dependencies = [ + "getrandom", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "faster-hex" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" +dependencies = [ + "serde", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fwdansi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208" +dependencies = [ + "memchr", + "termcolor", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "git2" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044" +dependencies = [ + "bitflags 1.3.2", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "git2-curl" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f8b7432b72928cff76f69e59ed5327f94a52763731e71274960dee72fe5f8c" +dependencies = [ + "curl", + "git2", + "log", + "url", +] + +[[package]] +name = "gix" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf2a03ec66ee24d1b2bae3ab718f8d14f141613810cb7ff6756f7db667f1cd82" +dependencies = [ + "gix-actor", + "gix-attributes", + "gix-commitgraph", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-discover", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-mailmap", + "gix-negotiate", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-prompt", + "gix-protocol", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-sec", + "gix-tempfile", + "gix-transport", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "log", + "once_cell", + "prodash", + "signal-hook", + "smallvec", + "thiserror", + "unicode-normalization", +] + +[[package]] +name = "gix-actor" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe73f9f6be1afbf1bd5be919a9636fa560e2f14d42262a934423ed6760cd838" +dependencies = [ + "bstr", + "btoi", + "gix-date", + "itoa", + "nom", + "thiserror", +] + +[[package]] +name = "gix-attributes" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b79590ac382f80d87e06416f5fcac6fee5d83dcb152a00ed0bdbaa988acc31" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "kstring", + "log", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-chunk" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c8751169961ba7640b513c3b24af61aa962c967aaf04116734975cd5af0c52" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-command" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-commitgraph" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8490ae1b3d55c47e6a71d247c082304a2f79f8d0332c1a2f5693d42a2021a09" +dependencies = [ + "bstr", + "gix-chunk", + "gix-features", + "gix-hash", + "memmap2", + "thiserror", +] + +[[package]] +name = "gix-config" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f310120ae1ba8f0ca52fb22876ce9bad5b15c8ffb3eb7302e4b64a3b9f681c" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "log", + "memchr", + "nom", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-config-value" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e874f41437441c02991dcea76990b9058fadfc54b02ab4dd06ab2218af43897" +dependencies = [ + "bitflags 2.5.0", + "bstr", + "gix-path", + "libc", + "thiserror", +] + +[[package]] +name = "gix-credentials" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f89fea8acd28f5ef8fa5042146f1637afd4d834bc8f13439d8fd1e5aca0d65" +dependencies = [ + "bstr", + "gix-command", + "gix-config-value", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-url", + "thiserror", +] + +[[package]] +name = "gix-date" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc164145670e9130a60a21670d9b6f0f4f8de04e5dd256c51fa5a0340c625902" +dependencies = [ + "bstr", + "itoa", + "thiserror", + "time", +] + +[[package]] +name = "gix-diff" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9029ad0083cc286a4bd2f5b3bf66bb66398abc26f2731a2824cd5edfc41a0e33" +dependencies = [ + "gix-hash", + "gix-object", + "imara-diff", + "thiserror", +] + +[[package]] +name = "gix-discover" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba9c6c0d1f2b2efe65581de73de4305004612d49c83773e783202a7ef204f46" +dependencies = [ + "bstr", + "dunce", + "gix-hash", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror", +] + +[[package]] +name = "gix-features" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8c493409bf6060d408eec9bbdd1b12ea351266b50012e2a522f75dfc7b8314" +dependencies = [ + "bytes", + "crc32fast", + "crossbeam-channel", + "flate2", + "gix-hash", + "libc", + "once_cell", + "parking_lot", + "prodash", + "sha1_smol", + "thiserror", + "walkdir", +] + +[[package]] +name = "gix-fs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30da8997008adb87f94e15beb7ee229f8a48e97af585a584bfee4a5a1880aab5" +dependencies = [ + "gix-features", +] + +[[package]] +name = "gix-glob" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0ade1e80ab1f079703d1824e1daf73009096386aa7fd2f0477f6e4ac0a558e" +dependencies = [ + "bitflags 2.5.0", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b422ff2ad9a0628baaad6da468cf05385bf3f5ab495ad5a33cce99b9f41092f" +dependencies = [ + "hex", + "thiserror", +] + +[[package]] +name = "gix-hashtable" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385f4ce6ecf3692d313ca3aa9bd3b3d8490de53368d6d94bedff3af8b6d9c58d" +dependencies = [ + "gix-hash", + "hashbrown 0.14.3", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6f7f101a0ccce808dbf7008ba131dede94e20257e7bde7a44cbb2f8c775625" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "unicode-bom", +] + +[[package]] +name = "gix-index" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ba958fabfb11263fa042c35690d48a6c7be4e9277e2c7e24ff263b3fe7b82" +dependencies = [ + "bitflags 2.5.0", + "bstr", + "btoi", + "filetime", + "gix-bitmap", + "gix-features", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "itoa", + "memmap2", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-lock" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ec5d5e6f07316d3553aa7425e3ecd935ec29882556021fe1696297a448af8d2" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-mailmap" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4653701922c920e009f1bc4309feaff14882ade017770788f9a150928da3fa6a" +dependencies = [ + "bstr", + "gix-actor", + "thiserror", +] + +[[package]] +name = "gix-negotiate" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945c3ef1e912e44a5f405fc9e924edf42000566a1b257ed52cb1293300f6f08c" +dependencies = [ + "bitflags 2.5.0", + "gix-commitgraph", + "gix-hash", + "gix-object", + "gix-revision", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-object" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8926c8f51c44dec3e709cb5dbc93deb9e8d4064c43c9efc54c158dcdfe8446c7" +dependencies = [ + "bstr", + "btoi", + "gix-actor", + "gix-features", + "gix-hash", + "gix-validate", + "hex", + "itoa", + "nom", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-odb" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b234d806278eeac2f907c8b5a105c4ba537230c1a9d9236d822bf0db291f8f3" +dependencies = [ + "arc-swap", + "gix-features", + "gix-hash", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "parking_lot", + "tempfile", + "thiserror", +] + +[[package]] +name = "gix-pack" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d2a14cb3156037eedb17d6cb7209b7180522b8949b21fd0fe3184c0a1d0af88" +dependencies = [ + "clru", + "gix-chunk", + "gix-diff", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-traverse", + "memmap2", + "parking_lot", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-packetline" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab" +dependencies = [ + "bstr", + "faster-hex", + "thiserror", +] + +[[package]] +name = "gix-path" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18609c8cbec8508ea97c64938c33cd305b75dfc04a78d0c3b78b8b3fd618a77c" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror", +] + +[[package]] +name = "gix-prompt" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c22decaf4a063ccae2b2108820c8630c01bd6756656df3fe464b32b8958a5ea" +dependencies = [ + "gix-command", + "gix-config-value", + "parking_lot", + "rustix", + "thiserror", +] + +[[package]] +name = "gix-protocol" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92a17058b45c461f0847528c5fb6ee6e76115e026979eb2d2202f98ee94f6c24" +dependencies = [ + "bstr", + "btoi", + "gix-credentials", + "gix-features", + "gix-hash", + "gix-transport", + "maybe-async", + "nom", + "thiserror", +] + +[[package]] +name = "gix-quote" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbff4f9b9ea3fa7a25a70ee62f545143abef624ac6aa5884344e70c8b0a1d9ff" +dependencies = [ + "bstr", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-ref" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebdd999256f4ce8a5eefa89999879c159c263f3493a951d62aa5ce42c0397e1c" +dependencies = [ + "gix-actor", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-validate", + "memmap2", + "nom", + "thiserror", +] + +[[package]] +name = "gix-refspec" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72bfd622abc86dd8ad1ec51b9eb77b4f1a766b94e3a1b87cf4a022c5b5570cf4" +dependencies = [ + "bstr", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-revision" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5044f56cd7a487ce9b034cbe0252ae0b6b47ff56ca3dabd79bc30214d0932cd7" +dependencies = [ + "bstr", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "thiserror", +] + +[[package]] +name = "gix-revwalk" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2623ba8747914f151f5e12b65adac576ab459dbed5f50a36c7a3e9cbf2d3ca" +dependencies = [ + "gix-commitgraph", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-sec" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9615cbd6b456898aeb942cd75e5810c382fbfc48dbbff2fa23ebd2d33dcbe9c7" +dependencies = [ + "bitflags 2.5.0", + "gix-path", + "libc", + "windows", +] + +[[package]] +name = "gix-tempfile" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3785cb010e9dc5c446dfbf02bc1119fc17d3a48a27c029efcb3a3c32953eb10" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "signal-hook", + "signal-hook-registry", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b838b2db8f62c9447d483a4c28d251b67fee32741a82cb4d35e9eb4e9fdc5ab" + +[[package]] +name = "gix-transport" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a39ffed9a9078ed700605e064b15d7c6ae50aa65e7faa36ca6919e8081df15" +dependencies = [ + "base64", + "bstr", + "curl", + "gix-command", + "gix-credentials", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "thiserror", +] + +[[package]] +name = "gix-traverse" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0842e984cb4bf26339dc559f3a1b8bf8cdb83547799b2b096822a59f87f33d9" +dependencies = [ + "gix-hash", + "gix-hashtable", + "gix-object", + "thiserror", +] + +[[package]] +name = "gix-url" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1663df25ac42047a2547618d2a6979a26f478073f6306997429235d2cd4c863" +dependencies = [ + "bstr", + "gix-features", + "gix-path", + "home", + "thiserror", + "url", +] + +[[package]] +name = "gix-utils" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066432d4c277f9877f091279a597ea5331f68ca410efc874f0bdfb1cd348f92" +dependencies = [ + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba9b3737b2cef3dcd014633485f0034b0f1a931ee54aeb7d8f87f177f3c89040" +dependencies = [ + "bstr", + "thiserror", +] + +[[package]] +name = "gix-worktree" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d388ad962e8854402734a7387af8790f6bdbc8d05349052dab16ca4a0def50f6" +dependencies = [ + "bstr", + "filetime", + "gix-attributes", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "io-close", + "thiserror", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http-auth" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643c9bbf6a4ea8a656d6b4cd53d34f79e3f841ad5203c1a55fb7d761923bc255" +dependencies = [ + "memchr", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "im-rc" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "imara-diff" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8" +dependencies = [ + "ahash", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "io-close" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libgit2-sys" +version = "0.15.2+1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libnghttp2-sys" +version = "0.1.9+1.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "miow" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opener" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" +dependencies = [ + "bstr", + "winapi", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "orion" +version = "0.17.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abdb10181903c8c4b016ba45d6d6d5af1a1e2a461aa4763a83b87f5df4695e5" +dependencies = [ + "fiat-crypto", + "subtle", + "zeroize", +] + +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pasetors" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b36d47c66f2230dd1b7143d9afb2b4891879020210eddf2ccb624e529b96dba" +dependencies = [ + "ct-codecs", + "ed25519-compact", + "getrandom", + "orion", + "p384", + "rand_core", + "regex", + "serde", + "serde_json", + "sha2", + "subtle", + "time", + "zeroize", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prodash" +version = "25.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d67eb4220992a4a052a4bb03cf776e493ecb1a3a36bab551804153d63486af7" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +dependencies = [ + "bitflags 2.5.0", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustfix" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481" +dependencies = [ + "anyhow", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_ignored" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e319a36d1b52126a0d608f24e93b2d81297091818cd70625fcf50a15d84ddf" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strip-ansi-escapes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +dependencies = [ + "vte", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/tools/external_crates/Cargo.toml b/tools/external_crates/Cargo.toml new file mode 100644 index 000000000..f0b017094 --- /dev/null +++ b/tools/external_crates/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "crate_health", + "crate_health_proc_macros", +] +resolver = "2" diff --git a/tools/external_crates/crate_health/Cargo.toml b/tools/external_crates/crate_health/Cargo.toml new file mode 100644 index 000000000..186385e26 --- /dev/null +++ b/tools/external_crates/crate_health/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "crate_health" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +cargo = "0.73" +clap = { version = "4.4.6", features = ["derive"] } +glob = "0.3" +itertools = "0.11" +num_cpus = "1" +semver = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" +threadpool = "1" +tinytemplate = "1.2" +walkdir = "2" +whoami = "1" +crate_health_proc_macros = { path = "../crate_health_proc_macros" } + +[dev-dependencies] +tempfile = "3" diff --git a/tools/external_crates/crate_health/src/android_bp.rs b/tools/external_crates/crate_health/src/android_bp.rs new file mode 100644 index 000000000..5b400d7c5 --- /dev/null +++ b/tools/external_crates/crate_health/src/android_bp.rs @@ -0,0 +1,119 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + collections::BTreeMap, + env, + path::Path, + process::{Command, Output}, + str::from_utf8, + sync::mpsc::channel, +}; + +use anyhow::{anyhow, Context, Result}; +use threadpool::ThreadPool; + +use crate::{Crate, NameAndVersion, NameAndVersionMap, NamedAndVersioned}; + +pub fn generate_android_bps<'a, T: Iterator<Item = &'a Crate>>( + crates: T, +) -> Result<BTreeMap<NameAndVersion, Output>> { + let pool = ThreadPool::new(std::cmp::max(num_cpus::get(), 32)); + let (tx, rx) = channel(); + + let mut num_crates = 0; + for krate in crates { + num_crates += 1; + let tx = tx.clone(); + let crate_name = krate.name().to_string(); + let crate_version = krate.version().clone(); + let repo_root = krate.root().to_path_buf(); + let test_path = krate.staging_path(); + pool.execute(move || { + tx.send((crate_name, crate_version, generate_android_bp(&repo_root, &test_path))) + .expect("Failed to send"); + }); + } + let mut results = BTreeMap::new(); + for (crate_name, crate_version, result) in rx.iter().take(num_crates) { + results.insert_or_error(NameAndVersion::new(crate_name, crate_version), result?)?; + } + Ok(results) +} + +pub(crate) fn generate_android_bp( + repo_root: &impl AsRef<Path>, + staging_path: &impl AsRef<Path>, +) -> Result<Output> { + let generate_android_bp_output = run_cargo_embargo(repo_root, staging_path)?; + if !generate_android_bp_output.status.success() { + println!( + "cargo_embargo failed for {}\nstdout:\n{}\nstderr:\n{}", + staging_path.as_ref().display(), + from_utf8(&generate_android_bp_output.stdout)?, + from_utf8(&generate_android_bp_output.stderr)? + ); + } + Ok(generate_android_bp_output) +} + +fn run_cargo_embargo( + repo_root: &impl AsRef<Path>, + staging_path: &impl AsRef<Path>, +) -> Result<Output> { + // Make sure we can find bpfmt. + let host_bin = repo_root.as_ref().join("out/host/linux-x86/bin"); + let new_path = match env::var_os("PATH") { + Some(p) => { + let mut paths = vec![host_bin]; + paths.extend(env::split_paths(&p)); + env::join_paths(paths)? + } + None => host_bin.as_os_str().into(), + }; + + let staging_path_absolute = repo_root.as_ref().join(staging_path); + let mut cmd = Command::new(repo_root.as_ref().join("out/host/linux-x86/bin/cargo_embargo")); + cmd.args(["generate", "cargo_embargo.json"]) + .env("PATH", new_path) + .env("ANDROID_BUILD_TOP", repo_root.as_ref()) + .current_dir(&staging_path_absolute) + .output() + .context(format!("Failed to execute {:?}", cmd.get_program())) +} + +pub fn maybe_build_cargo_embargo(repo_root: &impl AsRef<Path>, force_rebuild: bool) -> Result<()> { + if !force_rebuild + && repo_root.as_ref().join("out/host/linux-x86/bin/cargo_embargo").exists() + && repo_root.as_ref().join("out/host/linux-x86/bin/bpfmt").exists() + { + Ok(()) + } else { + println!("Rebuilding cargo_embargo"); + build_cargo_embargo(repo_root) + } +} + +pub fn build_cargo_embargo(repo_root: &impl AsRef<Path>) -> Result<()> { + let status = Command::new("/usr/bin/bash") + .args(["-c", "source build/envsetup.sh && lunch aosp_cf_x86_64_phone-trunk_staging-eng && m cargo_embargo bpfmt"]) + .current_dir(repo_root).spawn().context("Failed to spawn build of cargo embargo and bpfmt")?.wait().context("Failed to wait on child process building cargo embargo and bpfmt")?; + match status.success() { + true => Ok(()), + false => Err(anyhow!( + "Building cargo embargo and bpfmt failed with exit code {}", + status.code().map(|code| { format!("{}", code) }).unwrap_or("(unknown)".to_string()) + )), + } +} diff --git a/tools/external_crates/crate_health/src/bin/health_report.rs b/tools/external_crates/crate_health/src/bin/health_report.rs new file mode 100644 index 000000000..7a4ffdb94 --- /dev/null +++ b/tools/external_crates/crate_health/src/bin/health_report.rs @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::path::PathBuf; + +use anyhow::Result; +use clap::Parser; +use crate_health::{ + default_output_dir, default_repo_root, maybe_build_cargo_embargo, CrateCollection, + NameAndVersionMap, ReportEngine, +}; + +/// Generate a health report for crates in external/rust/crates +#[derive(Parser, Debug)] +#[command(about, long_about = None)] +struct Args { + /// Path to the AOSP repo. Defaults to current working directory. + #[arg(long, default_value_os_t=default_repo_root().unwrap_or(PathBuf::from(".")))] + repo_root: PathBuf, + + /// Path the health report will be written to. + #[arg(long, default_value_os_t=default_output_dir("crate-health-report.html"))] + output_path: PathBuf, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + maybe_build_cargo_embargo(&args.repo_root, false)?; + + let mut cc = CrateCollection::new(args.repo_root); + cc.add_from(&"external/rust/crates", None::<&&str>)?; + cc.map_field_mut().retain(|_nv, krate| krate.is_crates_io()); + + cc.stage_crates()?; + cc.generate_android_bps()?; + cc.diff_android_bps()?; + + let re = ReportEngine::new()?; + + Ok(re.health_report(&cc, &args.output_path)?) +} diff --git a/tools/external_crates/crate_health/src/bin/migration_report.rs b/tools/external_crates/crate_health/src/bin/migration_report.rs new file mode 100644 index 000000000..900d9aad5 --- /dev/null +++ b/tools/external_crates/crate_health/src/bin/migration_report.rs @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::path::PathBuf; + +use anyhow::Result; +use clap::Parser; +use crate_health::{ + default_output_dir, default_repo_root, maybe_build_cargo_embargo, migrate, ReportEngine, +}; + +/// Generate a health report for crates in external/rust/crates +#[derive(Parser, Debug)] +#[command(about, long_about = None)] +struct Args { + /// Path to the AOSP repo. Defaults to current working directory. + #[arg(long, default_value_os_t=default_repo_root().unwrap_or(PathBuf::from(".")))] + repo_root: PathBuf, + + /// Path the health report will be written to. + #[arg(long, default_value_os_t=default_output_dir("crate-migration-report.html"))] + output_path: PathBuf, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + maybe_build_cargo_embargo(&args.repo_root, false)?; + + let migration = + migrate(args.repo_root, &"external/rust/crates", &"out/rust-crate-migration-report")?; + + let re = ReportEngine::new()?; + + Ok(re.migration_report(&migration, &args.output_path)?) +} diff --git a/tools/external_crates/crate_health/src/crate_collection.rs b/tools/external_crates/crate_health/src/crate_collection.rs new file mode 100644 index 000000000..413b5bc1e --- /dev/null +++ b/tools/external_crates/crate_health/src/crate_collection.rs @@ -0,0 +1,99 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate_health_proc_macros::NameAndVersionMap; + +use std::{ + collections::HashSet, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, Result}; +use semver::Version; +use walkdir::WalkDir; + +use crate::{ + android_bp::generate_android_bps, Crate, CrateError, NameAndVersion, NameAndVersionMap, + NamedAndVersioned, +}; + +use std::collections::BTreeMap; + +#[derive(NameAndVersionMap)] +pub struct CrateCollection { + crates: BTreeMap<NameAndVersion, Crate>, + repo_root: PathBuf, +} + +impl CrateCollection { + pub fn new<P: Into<PathBuf>>(repo_root: P) -> CrateCollection { + CrateCollection { crates: BTreeMap::new(), repo_root: repo_root.into() } + } + pub fn add_from( + &mut self, + path: &impl AsRef<Path>, + pseudo_crate: Option<&impl AsRef<Path>>, + ) -> Result<()> { + for entry_or_err in WalkDir::new(self.repo_root.join(path)) { + let entry = entry_or_err?; + if entry.file_name() == "Cargo.toml" { + match Crate::from( + &entry.path(), + &self.repo_root.as_path(), + pseudo_crate.map(|p| p.as_ref()), + ) { + Ok(krate) => self.crates.insert_or_error( + NameAndVersion::new(krate.name().to_string(), krate.version().clone()), + krate, + )?, + Err(e) => match e.downcast_ref() { + Some(CrateError::VirtualCrate(_)) => (), + _ => return Err(e), + }, + }; + } + } + Ok(()) + } + pub fn repo_root(&self) -> &Path { + self.repo_root.as_path() + } + pub fn print(&self) -> Result<()> { + for krate in self.crates.values() { + krate.print()? + } + Ok(()) + } + pub fn stage_crates(&self) -> Result<()> { + for krate in self.crates.values() { + krate.stage_crate()? + } + Ok(()) + } + pub fn generate_android_bps(&mut self) -> Result<()> { + for (nv, output) in generate_android_bps(self.crates.values())?.into_iter() { + self.crates + .get_mut(&nv) + .ok_or(anyhow!("Failed to get crate {} {}", nv.name(), nv.version()))? + .set_generate_android_bp_output(output); + } + Ok(()) + } + pub fn diff_android_bps(&mut self) -> Result<()> { + for krate in self.crates.values_mut() { + krate.diff_android_bp()?; + } + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/crate_type.rs b/tools/external_crates/crate_health/src/crate_type.rs new file mode 100644 index 000000000..c2d93a949 --- /dev/null +++ b/tools/external_crates/crate_health/src/crate_type.rs @@ -0,0 +1,370 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + fs::{read_dir, remove_dir_all}, + path::{Path, PathBuf}, + process::{Command, Output}, + str::from_utf8, +}; + +use anyhow::{anyhow, Context, Result}; +use cargo::{ + core::{Manifest, SourceId}, + util::toml::read_manifest, + Config, +}; +use semver::Version; + +use crate::{ + copy_dir, ensure_exists_and_empty, name_and_version::IsUpgradableTo, CrateError, + NameAndVersionRef, NamedAndVersioned, +}; + +#[derive(Debug)] +pub struct Crate { + manifest: Manifest, + + // root is absolute. All other paths are relative to it. + root: PathBuf, + relpath: PathBuf, + pseudo_crate: Option<PathBuf>, + + // compatible_dest_version: Option<Version>, + patch_output: Vec<Output>, + generate_android_bp_output: Option<Output>, + android_bp_diff: Option<Output>, +} + +impl NamedAndVersioned for Crate { + fn name(&self) -> &str { + self.manifest.name().as_str() + } + fn version(&self) -> &Version { + self.manifest.version() + } + fn key<'k>(&'k self) -> NameAndVersionRef<'k> { + NameAndVersionRef::new(self.name(), self.version()) + } +} + +impl IsUpgradableTo for Crate {} + +impl Crate { + pub fn new<P: Into<PathBuf>, Q: Into<PathBuf>, R: Into<PathBuf>>( + manifest: Manifest, + root: P, + relpath: Q, + pseudo_crate: Option<R>, + ) -> Crate { + Crate { + manifest, + root: root.into(), + relpath: relpath.into(), + pseudo_crate: pseudo_crate.map(|p| p.into()), + // compatible_dest_version: None, + patch_output: Vec::new(), + generate_android_bp_output: None, + android_bp_diff: None, + } + } + pub fn from<P: Into<PathBuf>, Q: Into<PathBuf>>( + cargo_toml: &impl AsRef<Path>, + root: P, + pseudo_crate: Option<Q>, + ) -> Result<Crate> { + let root: PathBuf = root.into(); + let manifest_dir = cargo_toml.as_ref().parent().ok_or(anyhow!( + "Failed to get parent directory of manifest at {}", + cargo_toml.as_ref().display() + ))?; + let relpath = manifest_dir.strip_prefix(&root)?.to_path_buf(); + let source_id = SourceId::for_path(manifest_dir)?; + let (manifest, _nested) = + read_manifest(cargo_toml.as_ref(), source_id, &Config::default()?)?; + match manifest { + cargo::core::EitherManifest::Real(r) => Ok(Crate::new(r, root, relpath, pseudo_crate)), + cargo::core::EitherManifest::Virtual(_) => { + Err(anyhow!(CrateError::VirtualCrate(cargo_toml.as_ref().to_path_buf()))) + } + } + } + + pub fn root(&self) -> &Path { + self.root.as_path() + } + pub fn relpath(&self) -> &Path { + &self.relpath.as_path() + } + pub fn path(&self) -> PathBuf { + self.root.join(&self.relpath) + } + pub fn android_bp(&self) -> PathBuf { + self.relpath().join("Android.bp") + } + pub fn cargo_embargo_json(&self) -> PathBuf { + self.path().join("cargo_embargo.json") + } + pub fn staging_path(&self) -> PathBuf { + Path::new("out/rust-crate-temporary-build").join(self.staging_dir_name()) + } + pub fn patch_dir(&self) -> PathBuf { + self.staging_path().join("patches") + } + pub fn staging_dir_name(&self) -> String { + if let Some(dirname) = self.relpath.file_name().and_then(|x| x.to_str()) { + if dirname == self.name() { + return dirname.to_string(); + } + } + format!("{}-{}", self.name(), self.version().to_string()) + } + + pub fn aosp_url(&self) -> Option<String> { + if self.relpath.starts_with("external/rust/crates") { + if self.relpath.ends_with(self.name()) { + Some(format!( + "https://android.googlesource.com/platform/{}/+/refs/heads/main", + self.relpath().display() + )) + } else if self.relpath.parent()?.ends_with(self.name()) { + Some(format!( + "https://android.googlesource.com/platform/{}/+/refs/heads/main/{}", + self.relpath().parent()?.display(), + self.relpath().file_name()?.to_str()? + )) + } else { + None + } + } else { + None + } + } + pub fn crates_io_url(&self) -> String { + format!("https://crates.io/crates/{}", self.name()) + } + + pub fn is_vendored(&self) -> bool { + self.pseudo_crate.is_some() + } + pub fn is_crates_io(&self) -> bool { + const NOT_CRATES_IO: &'static [&'static str] = &[ + "external/rust/beto-rust/", // Google crates + "external/rust/pica/", // Google crate + "external/rust/crates/webpki/third-party/", // Internal/example code + "external/rust/cxx/third-party/", // Internal/example code + "external/rust/cxx/demo/", // Internal/example code + ]; + !NOT_CRATES_IO.iter().any(|prefix| self.relpath.starts_with(prefix)) + } + pub fn is_migration_denied(&self) -> bool { + const MIGRATION_DENYLIST: &'static [&'static str] = &[ + "external/rust/crates/openssl/", // It's complicated. + "external/rust/cxx/", // It's REALLY complicated. + ]; + MIGRATION_DENYLIST.iter().any(|prefix| self.relpath.starts_with(prefix)) + } + pub fn is_android_bp_healthy(&self) -> bool { + !self.is_migration_denied() + && self.root().join(self.android_bp()).exists() + && self.cargo_embargo_json().exists() + && self.generate_android_bp_success() + && self.android_bp_unchanged() + } + pub fn patch_success(&self) -> bool { + self.patch_output.iter().all(|output| output.status.success()) + } + pub fn generate_android_bp_success(&self) -> bool { + self.generate_android_bp_output.as_ref().is_some_and(|output| output.status.success()) + } + pub fn android_bp_unchanged(&self) -> bool { + self.android_bp_diff.as_ref().is_some_and(|output| output.status.success()) + } + + pub fn print(&self) -> Result<()> { + println!("{} {} {}", self.name(), self.version(), self.relpath.display()); + if let Some(output) = &self.generate_android_bp_output { + println!("generate Android.bp exit status: {}", output.status); + println!("{}", from_utf8(&output.stdout)?); + println!("{}", from_utf8(&output.stderr)?); + } + if let Some(output) = &self.android_bp_diff { + println!("diff exit status: {}", output.status); + println!("{}", from_utf8(&output.stdout)?); + println!("{}", from_utf8(&output.stderr)?); + } + Ok(()) + } + + // Make a clean copy of the crate in out/ + pub fn stage_crate(&self) -> Result<()> { + let staging_path_absolute = self.root().join(self.staging_path()); + ensure_exists_and_empty(&staging_path_absolute)?; + remove_dir_all(&staging_path_absolute) + .context(format!("Failed to remove {}", staging_path_absolute.display()))?; + copy_dir(&self.path(), &staging_path_absolute).context(format!( + "Failed to copy {} to {}", + self.path().display(), + staging_path_absolute.display() + ))?; + if staging_path_absolute.join(".git").is_dir() { + remove_dir_all(staging_path_absolute.join(".git")) + .with_context(|| "Failed to remove .git".to_string())?; + } + Ok(()) + } + + pub fn diff_android_bp(&mut self) -> Result<()> { + self.set_diff_output( + diff_android_bp( + &self.android_bp(), + &self.staging_path().join("Android.bp"), + &self.root(), + ) + .context("Failed to diff Android.bp".to_string())?, + ); + Ok(()) + } + + pub fn apply_patches(&mut self) -> Result<()> { + let patch_dir_absolute = self.root().join(self.patch_dir()); + if patch_dir_absolute.exists() { + for entry in read_dir(&patch_dir_absolute) + .context(format!("Failed to read_dir {}", patch_dir_absolute.display()))? + { + let entry = entry?; + if entry.file_name() == "Android.bp.patch" + || entry.file_name() == "Android.bp.diff" + || entry.file_name() == "rules.mk.diff" + { + continue; + } + let entry_path = entry.path(); + let output = Command::new("patch") + .args(["-p1", "-l", "--no-backup-if-mismatch", "-i"]) + .arg(&entry_path) + .current_dir(self.root().join(self.staging_path())) + .output()?; + if !output.status.success() { + println!( + "Failed to apply {}\nstdout:\n{}\nstderr:\n:{}", + entry_path.display(), + from_utf8(&output.stdout)?, + from_utf8(&output.stderr)? + ); + } + self.patch_output.push(output); + } + } + Ok(()) + } + + pub fn android_bp_diff(&self) -> Option<&Output> { + self.android_bp_diff.as_ref() + } + pub fn generate_android_bp_output(&self) -> Option<&Output> { + self.generate_android_bp_output.as_ref() + } + pub fn set_generate_android_bp_output(&mut self, c2a_output: Output) { + self.generate_android_bp_output.replace(c2a_output); + } + pub fn set_diff_output(&mut self, diff_output: Output) { + self.android_bp_diff.replace(diff_output); + } + pub fn set_patch_output(&mut self, patch_output: Vec<Output>) { + self.patch_output = patch_output; + } +} + +pub trait Migratable { + fn is_migration_eligible(&self) -> bool; + fn is_migratable(&self) -> bool; +} + +impl Migratable for Crate { + fn is_migration_eligible(&self) -> bool { + self.is_crates_io() + && !self.is_migration_denied() + && self.root.join(self.android_bp()).exists() + && self.cargo_embargo_json().exists() + } + fn is_migratable(&self) -> bool { + self.patch_success() && self.generate_android_bp_success() && self.android_bp_unchanged() + } +} + +#[cfg(test)] +mod tests { + use std::fs::{create_dir, write}; + + use super::*; + use anyhow::anyhow; + use tempfile::tempdir; + + fn write_test_manifest(temp_crate_dir: &Path, name: &str, version: &str) -> Result<PathBuf> { + let cargo_toml: PathBuf = [temp_crate_dir, &Path::new("Cargo.toml")].iter().collect(); + write( + cargo_toml.as_path(), + format!("[package]\nname = \"{}\"\nversion = \"{}\"\n", name, version), + )?; + let lib_rs: PathBuf = [temp_crate_dir, &Path::new("src/lib.rs")].iter().collect(); + create_dir(lib_rs.parent().ok_or(anyhow!("Failed to get parent"))?)?; + write(lib_rs.as_path(), "// foo")?; + Ok(cargo_toml) + } + + #[test] + fn test_from_and_properties() -> Result<()> { + let temp_crate_dir = tempdir()?; + let cargo_toml = write_test_manifest(temp_crate_dir.path(), "foo", "1.2.0")?; + let krate = Crate::from(&cargo_toml, &"/", None::<&&str>)?; + assert_eq!(krate.name(), "foo"); + assert_eq!(krate.version().to_string(), "1.2.0"); + assert!(krate.is_crates_io()); + assert_eq!(krate.root().join(krate.android_bp()), temp_crate_dir.path().join("Android.bp")); + assert_eq!(krate.cargo_embargo_json(), temp_crate_dir.path().join("cargo_embargo.json")); + Ok(()) + } + + #[test] + fn test_from_error() -> Result<()> { + let temp_crate_dir = tempdir()?; + let cargo_toml = write_test_manifest(temp_crate_dir.path(), "foo", "1.2.0")?; + assert!(Crate::from(&cargo_toml, &"/blah", None::<&&str>).is_err()); + Ok(()) + } +} + +pub fn diff_android_bp( + a: &impl AsRef<Path>, + b: &impl AsRef<Path>, + root: &impl AsRef<Path>, +) -> Result<Output> { + Ok(Command::new("diff") + .args([ + "-u", + "-w", + "-B", + "-I", + "// has rustc warnings", + "-I", + "This file is generated by", + "-I", + "cargo_pkg_version:", + ]) + .arg(a.as_ref()) + .arg(b.as_ref()) + .current_dir(root) + .output()?) +} diff --git a/tools/external_crates/crate_health/src/lib.rs b/tools/external_crates/crate_health/src/lib.rs new file mode 100644 index 000000000..c6e66a0b6 --- /dev/null +++ b/tools/external_crates/crate_health/src/lib.rs @@ -0,0 +1,110 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::env::current_dir; +use std::fs::{create_dir_all, remove_dir_all}; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::str::from_utf8; + +use anyhow::{anyhow, Context, Result}; +use semver::Version; +use thiserror::Error; + +pub use self::crate_type::{diff_android_bp, Crate, Migratable}; +mod crate_type; + +pub use self::crate_collection::CrateCollection; +mod crate_collection; + +pub use self::reports::{ReportEngine, SizeReport, Table}; +mod reports; + +pub use self::migration::migrate; +mod migration; + +pub use self::pseudo_crate::PseudoCrate; +mod pseudo_crate; + +pub use self::version_match::{CompatibleVersionPair, VersionMatch, VersionPair}; +mod version_match; + +pub use self::android_bp::{build_cargo_embargo, generate_android_bps, maybe_build_cargo_embargo}; +mod android_bp; + +pub use self::name_and_version::{ + IsUpgradableTo, NameAndVersion, NameAndVersionRef, NamedAndVersioned, +}; +mod name_and_version; + +#[cfg(test)] +pub use self::name_and_version_map::try_name_version_map_from_iter; +pub use self::name_and_version_map::{ + crates_with_multiple_versions, crates_with_single_version, most_recent_version, + NameAndVersionMap, +}; +mod name_and_version_map; + +#[derive(Error, Debug)] +pub enum CrateError { + #[error("Virtual crate: {0}")] + VirtualCrate(PathBuf), + + #[error("Duplicate crate version: {0} {1}")] + DuplicateCrateVersion(String, Version), +} + +pub fn default_repo_root() -> Result<PathBuf> { + let cwd = current_dir().context("Could not get current working directory")?; + for cur in cwd.ancestors() { + for e in cur.read_dir()? { + if e?.file_name() == ".repo" { + return Ok(cur.to_path_buf()); + } + } + } + Err(anyhow!(".repo directory not found in any ancestor of {}", cwd.display())) +} + +pub fn default_output_dir(filename: &str) -> PathBuf { + PathBuf::from("/google/data/rw/users") + .join(&whoami::username()[..2]) + .join(whoami::username()) + .join("www") + .join(filename) +} + +pub fn ensure_exists_and_empty(dir: &impl AsRef<Path>) -> Result<()> { + let dir = dir.as_ref(); + if dir.exists() { + remove_dir_all(&dir).context(format!("Failed to remove {}", dir.display()))?; + } + create_dir_all(&dir).context(format!("Failed to create {}", dir.display())) +} + +// The copy_dir crate doesn't handle symlinks. +pub fn copy_dir(src: &impl AsRef<Path>, dst: &impl AsRef<Path>) -> Result<()> { + let output = + Command::new("cp").arg("--archive").arg(src.as_ref()).arg(dst.as_ref()).output()?; + if !output.status.success() { + return Err(anyhow!( + "Failed to copy {} to {}\nstdout:\n{}\nstderr:\n{}", + src.as_ref().display(), + dst.as_ref().display(), + from_utf8(&output.stdout)?, + from_utf8(&output.stderr)? + )); + } + Ok(()) +} diff --git a/tools/external_crates/crate_health/src/main.rs b/tools/external_crates/crate_health/src/main.rs new file mode 100644 index 000000000..15d8865bf --- /dev/null +++ b/tools/external_crates/crate_health/src/main.rs @@ -0,0 +1,281 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{path::PathBuf, process::Command, str::from_utf8}; + +use anyhow::{anyhow, Result}; +use clap::{Parser, Subcommand}; +use crate_health::{ + default_repo_root, maybe_build_cargo_embargo, migrate, CrateCollection, Migratable, + NameAndVersionMap, NamedAndVersioned, +}; + +#[derive(Parser)] +struct Cli { + #[command(subcommand)] + command: Cmd, + + #[arg(long, default_value_os_t=default_repo_root().unwrap_or(PathBuf::from(".")))] + repo_root: PathBuf, + + /// Rebuild cargo_embargo and bpfmt, even if they are already present in the out directory. + #[arg(long, default_value_t = false)] + rebuild_cargo_embargo: bool, +} + +#[derive(Subcommand)] +enum Cmd { + /// Check the health of a crate, and whether it is safe to migrate. + MigrationHealth { + /// The crate name. Also the directory name in external/rust/crates + crate_name: String, + }, + /// Migrate a crate from external/rust/crates to the monorepo. + Migrate { + /// The crate name. Also the directory name in external/rust/crates + crate_name: String, + }, + Regenerate { + /// The crate name. + crate_name: String, + }, + /// Run pre-upload checks. + PreuploadCheck { + /// List of changed files + files: Vec<String>, + }, +} + +static IGNORED_FILES: &'static [&'static str] = &[ + ".appveyor.yml", + ".bazelci", + ".bazelignore", + ".bazelrc", + ".bazelversion", + ".buildkite", + ".cargo", + ".cargo-checksum.json", + ".cargo_vcs_info.json", + ".circleci", + ".cirrus.yml", + ".clang-format", + ".clang-tidy", + ".clippy.toml", + ".clog.toml", + ".clog.toml", + ".codecov.yaml", + ".codecov.yml", + ".editorconfig", + ".gcloudignore", + ".gdbinit", + ".git", + ".git-blame-ignore-revs", + ".git-ignore-revs", + ".gitallowed", + ".gitattributes", + ".github", + ".gitignore", + ".idea", + ".ignore", + ".istanbul.yml", + ".mailmap", + ".md-inc.toml", + ".mdl-style.rb", + ".mdlrc", + ".pylintrc", + ".pylintrc-examples", + ".pylintrc-tests", + ".reuse", + ".rspec", + ".rustfmt.toml", + ".shellcheckrc", + ".standard-version", + ".tarpaulin.toml", + ".tokeignore", + ".travis.yml", + ".versionrc", + ".vim", + ".vscode", + ".yapfignore", + ".yardopts", + "BUILD", + "Cargo.lock", + "Cargo.lock.saved", + "Cargo.toml.orig", + "OWNERS", + // rules.mk related files that we won't migrate. + "cargo2rulesmk.json", + "CleanSpec.mk", + "rules.mk", + // cargo_embargo intermediates. + "Android.bp.orig", + "cargo.metadata", + "cargo.out", + "target.tmp", +]; + +fn main() -> Result<()> { + let args = Cli::parse(); + + maybe_build_cargo_embargo(&args.repo_root, args.rebuild_cargo_embargo)?; + + match args.command { + Cmd::MigrationHealth { crate_name } => { + if args + .repo_root + .join("external/rust/android-crates-io/crates") + .join(&crate_name) + .exists() + { + return Err(anyhow!( + "Crate {} already exists in external/rust/android-crates-io/crates", + crate_name + )); + } + + let mut cc = CrateCollection::new(&args.repo_root); + cc.add_from(&PathBuf::from("external/rust/crates").join(&crate_name), None::<&&str>)?; + cc.map_field_mut().retain(|_nv, krate| krate.is_crates_io()); + if cc.map_field().len() != 1 { + return Err(anyhow!( + "Expected a single crate version for {}, but found {}. Crates with multiple versions are not supported yet.", + crate_name, + cc.map_field().len() + )); + } + + cc.stage_crates()?; + cc.generate_android_bps()?; + cc.diff_android_bps()?; + + let krate = cc.map_field().values().next().unwrap(); + println!( + "Found {} v{} in {}", + krate.name(), + krate.version(), + krate.relpath().display() + ); + let migratable; + if !krate.is_android_bp_healthy() { + if krate.is_migration_denied() { + println!("This crate is on the migration denylist"); + } + if !krate.root().join(krate.android_bp()).exists() { + println!("There is no Android.bp file in {}", krate.relpath().display()); + } + if !krate.cargo_embargo_json().exists() { + println!( + "There is no cargo_embargo.json file in {}", + krate.relpath().display() + ); + } else if !krate.generate_android_bp_success() { + println!( + "cargo_embargo execution did not succeed for {}", + krate.relpath().display() + ); + } else if !krate.android_bp_unchanged() { + println!( + "Running cargo_embargo on {} produced changes to the Android.bp file:", + krate.relpath().display() + ); + println!( + "{}", + from_utf8( + &krate + .android_bp_diff() + .ok_or(anyhow!("No Android.bp diff found"))? + .stdout + )? + ); + } + migratable = false; + } else { + let migration = migrate( + &args.repo_root, + &PathBuf::from("external/rust/crates").join(&crate_name), + &"out/rust-crate-migration-report", + )?; + let compatible_pairs = migration.compatible_pairs().collect::<Vec<_>>(); + if compatible_pairs.len() != 1 { + return Err(anyhow!("Couldn't find a compatible version to migrate to",)); + } + let pair = compatible_pairs.first().unwrap(); + if pair.source.version() != pair.dest.version() { + println!( + "Source and destination versions are different: {} -> {}", + pair.source.version(), + pair.dest.version() + ); + } + if !pair.dest.is_migratable() { + if !pair.dest.patch_success() { + println!("Patches did not apply successfully to the migrated crate"); + // TODO: Show errors. + } + if !pair.dest.generate_android_bp_success() { + println!("cargo_embargo execution did not succeed for the migrated crate"); + } else if pair.dest.android_bp_unchanged() { + println!("Running cargo_embargo for the migrated crate produced changes to the Android.bp file:"); + println!( + "{}", + from_utf8( + &pair + .dest + .android_bp_diff() + .ok_or(anyhow!("No Android.bp diff found"))? + .stdout + )? + ); + } + } + + let diff_status = Command::new("diff") + .args(["-u", "-r", "-w", "--no-dereference"]) + .args(IGNORED_FILES.iter().map(|ignored| format!("--exclude={}", ignored))) + .arg(pair.source.relpath()) + .arg(pair.dest.staging_path()) + .current_dir(&args.repo_root) + .spawn()? + .wait()?; + if !diff_status.success() { + println!( + "Found differences between {} and {}", + pair.source.relpath().display(), + pair.dest.staging_path().display() + ); + } + println!("All diffs:"); + Command::new("diff") + .args(["-u", "-r", "-w", "-q", "--no-dereference"]) + .arg(pair.source.relpath()) + .arg(pair.dest.staging_path()) + .current_dir(&args.repo_root) + .spawn()? + .wait()?; + + migratable = pair.dest.is_migratable() && diff_status.success() + } + + println!( + "The crate is {}", + if krate.is_android_bp_healthy() && migratable { "healthy" } else { "UNHEALTHY" } + ); + } + Cmd::Migrate { crate_name: _ } => todo!(), + Cmd::Regenerate { crate_name: _ } => todo!(), + Cmd::PreuploadCheck { files: _ } => todo!(), + } + + Ok(()) +} diff --git a/tools/external_crates/crate_health/src/migration.rs b/tools/external_crates/crate_health/src/migration.rs new file mode 100644 index 000000000..318a4c01e --- /dev/null +++ b/tools/external_crates/crate_health/src/migration.rs @@ -0,0 +1,128 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + fs::{copy, read_link, remove_dir_all}, + os::unix::fs::symlink, + path::{Path, PathBuf}, + process::Output, +}; + +use anyhow::{anyhow, Context, Result}; +use glob::glob; + +use crate::{ + copy_dir, crate_type::diff_android_bp, most_recent_version, CompatibleVersionPair, Crate, + CrateCollection, Migratable, NameAndVersionMap, PseudoCrate, VersionMatch, +}; + +static CUSTOMIZATIONS: &'static [&'static str] = + &["*.bp", "cargo_embargo.json", "patches", "METADATA", "TEST_MAPPING", "MODULE_LICENSE_*"]; + +static SYMLINKS: &'static [&'static str] = &["LICENSE", "NOTICE"]; + +impl<'a> CompatibleVersionPair<'a, Crate> { + pub fn copy_customizations(&self) -> Result<()> { + let dest_dir_absolute = self.dest.root().join(self.dest.staging_path()); + for pattern in CUSTOMIZATIONS { + let full_pattern = self.source.path().join(pattern); + for entry in glob( + full_pattern + .to_str() + .ok_or(anyhow!("Failed to convert path {} to str", full_pattern.display()))?, + )? { + let entry = entry?; + let filename = entry + .file_name() + .context(format!("Failed to get file name for {}", entry.display()))? + .to_os_string(); + if entry.is_dir() { + copy_dir(&entry, &dest_dir_absolute.join(filename)).context(format!( + "Failed to copy {} to {}", + entry.display(), + dest_dir_absolute.display() + ))?; + } else { + let dest_file = dest_dir_absolute.join(&filename); + if dest_file.exists() { + return Err(anyhow!("Destination file {} exists", dest_file.display())); + } + copy(&entry, dest_dir_absolute.join(filename)).context(format!( + "Failed to copy {} to {}", + entry.display(), + dest_dir_absolute.display() + ))?; + } + } + } + for link in SYMLINKS { + let src_path = self.source.path().join(link); + if src_path.is_symlink() { + let dest = read_link(src_path)?; + if dest.exists() { + return Err(anyhow!( + "Can't symlink {} -> {} because destination exists", + link, + dest.display(), + )); + } + symlink(dest, dest_dir_absolute.join(link))?; + } + } + Ok(()) + } + pub fn diff_android_bps(&self) -> Result<Output> { + diff_android_bp( + &self.source.android_bp(), + &self.dest.staging_path().join("Android.bp"), + &self.source.root(), + ) + .context("Failed to diff Android.bp".to_string()) + } +} + +pub fn migrate<P: Into<PathBuf>>( + repo_root: P, + source_dir: &impl AsRef<Path>, + pseudo_crate_dir: &impl AsRef<Path>, +) -> Result<VersionMatch<CrateCollection>> { + let mut source = CrateCollection::new(repo_root); + source.add_from(source_dir, None::<&&str>)?; + source.map_field_mut().retain(|_nv, krate| krate.is_crates_io()); + + let pseudo_crate = PseudoCrate::new(source.repo_root().join(pseudo_crate_dir)); + if pseudo_crate.get_path().exists() { + remove_dir_all(pseudo_crate.get_path()) + .context(format!("Failed to remove {}", pseudo_crate.get_path().display()))?; + } + pseudo_crate.init( + source + .filter_versions(&most_recent_version) + .filter(|(_nv, krate)| krate.is_migration_eligible()) + .map(|(_nv, krate)| krate), + )?; + + let mut dest = CrateCollection::new(source.repo_root()); + dest.add_from(&pseudo_crate_dir.as_ref().join("vendor"), Some(pseudo_crate_dir))?; + + let mut version_match = VersionMatch::new(source, dest)?; + + version_match.stage_crates()?; + version_match.copy_customizations()?; + version_match.apply_patches()?; + version_match.generate_android_bps()?; + version_match.diff_android_bps()?; + + Ok(version_match) +} diff --git a/tools/external_crates/crate_health/src/name_and_version.rs b/tools/external_crates/crate_health/src/name_and_version.rs new file mode 100644 index 000000000..90dc24319 --- /dev/null +++ b/tools/external_crates/crate_health/src/name_and_version.rs @@ -0,0 +1,189 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + borrow::Borrow, + cmp::Ordering, + hash::{Hash, Hasher}, +}; + +#[cfg(test)] +use anyhow::Result; + +use semver::{BuildMetadata, Prerelease, Version, VersionReq}; + +static MIN_VERSION: Version = + Version { major: 0, minor: 0, patch: 0, pre: Prerelease::EMPTY, build: BuildMetadata::EMPTY }; + +pub trait NamedAndVersioned { + fn name(&self) -> &str; + fn version(&self) -> &Version; + fn key<'a>(&'a self) -> NameAndVersionRef<'a>; +} + +#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] +pub struct NameAndVersion { + name: String, + version: Version, +} + +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct NameAndVersionRef<'a> { + name: &'a str, + version: &'a Version, +} + +impl NameAndVersion { + pub fn new(name: String, version: Version) -> Self { + NameAndVersion { name, version } + } + pub fn from(nv: &impl NamedAndVersioned) -> Self { + NameAndVersion { name: nv.name().to_string(), version: nv.version().clone() } + } + pub fn min_version(name: String) -> Self { + NameAndVersion { name, version: MIN_VERSION.clone() } + } + #[cfg(test)] + pub fn try_from_str(name: &str, version: &str) -> Result<Self> { + Ok(NameAndVersion::new(name.to_string(), Version::parse(version)?)) + } +} + +impl NamedAndVersioned for NameAndVersion { + fn name(&self) -> &str { + self.name.as_str() + } + + fn version(&self) -> &Version { + &self.version + } + fn key<'k>(&'k self) -> NameAndVersionRef<'k> { + NameAndVersionRef::new(self.name(), self.version()) + } +} + +impl<'a> NameAndVersionRef<'a> { + pub fn new(name: &'a str, version: &'a Version) -> Self { + NameAndVersionRef { name, version } + } +} + +impl<'a> NamedAndVersioned for NameAndVersionRef<'a> { + fn name(&self) -> &str { + self.name + } + fn version(&self) -> &Version { + self.version + } + fn key<'k>(&'k self) -> NameAndVersionRef<'k> { + *self + } +} + +impl<'a> Borrow<dyn NamedAndVersioned + 'a> for NameAndVersion { + fn borrow(&self) -> &(dyn NamedAndVersioned + 'a) { + self + } +} + +impl<'a> PartialEq for (dyn NamedAndVersioned + 'a) { + fn eq(&self, other: &Self) -> bool { + self.key().eq(&other.key()) + } +} + +impl<'a> Eq for (dyn NamedAndVersioned + 'a) {} + +impl<'a> PartialOrd for (dyn NamedAndVersioned + 'a) { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.key().partial_cmp(&other.key()) + } +} + +impl<'a> Ord for (dyn NamedAndVersioned + 'a) { + fn cmp(&self, other: &Self) -> Ordering { + self.key().cmp(&other.key()) + } +} + +impl<'a> Hash for (dyn NamedAndVersioned + 'a) { + fn hash<H: Hasher>(&self, state: &mut H) { + self.key().hash(state) + } +} + +pub trait IsUpgradableTo: NamedAndVersioned { + fn is_upgradable_to(&self, other: &impl NamedAndVersioned) -> bool { + self.name() == other.name() + && VersionReq::parse(&self.version().to_string()) + .is_ok_and(|req| req.matches(other.version())) + } +} + +impl<'a> IsUpgradableTo for NameAndVersion {} +impl<'a> IsUpgradableTo for NameAndVersionRef<'a> {} + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::Result; + + #[test] + fn test_name_version_ref() -> Result<()> { + let version = Version::parse("2.3.4")?; + let compat1 = Version::parse("2.3.5")?; + let compat2 = Version::parse("2.4.0")?; + let incompat = Version::parse("3.0.0")?; + let older = Version::parse("2.3.3")?; + let nvp = NameAndVersionRef::new("foo", &version); + assert_eq!(nvp.name(), "foo"); + assert_eq!(nvp.version().to_string(), "2.3.4"); + assert!(nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat1)), "Patch update"); + assert!( + nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat2)), + "Minor version update" + ); + assert!( + !nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &incompat)), + "Incompatible (major version) update" + ); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &older)), "Downgrade"); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("bar", &compat1)), "Different name"); + Ok(()) + } + + #[test] + fn test_name_and_version() -> Result<()> { + let version = Version::parse("2.3.4")?; + let compat1 = Version::parse("2.3.5")?; + let compat2 = Version::parse("2.4.0")?; + let incompat = Version::parse("3.0.0")?; + let older = Version::parse("2.3.3")?; + let nvp = NameAndVersion::new("foo".to_string(), version); + assert_eq!(nvp.name(), "foo"); + assert_eq!(nvp.version().to_string(), "2.3.4"); + assert!(nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat1)), "Patch update"); + assert!( + nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &compat2)), + "Minor version update" + ); + assert!( + !nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &incompat)), + "Incompatible (major version) update" + ); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("foo", &older)), "Downgrade"); + assert!(!nvp.is_upgradable_to(&NameAndVersionRef::new("bar", &compat1)), "Different name"); + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/name_and_version_map.rs b/tools/external_crates/crate_health/src/name_and_version_map.rs new file mode 100644 index 000000000..f35d40415 --- /dev/null +++ b/tools/external_crates/crate_health/src/name_and_version_map.rs @@ -0,0 +1,289 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::{BTreeMap, HashSet}; + +use anyhow::Result; +use itertools::Itertools; +use semver::Version; + +use crate::{CrateError, IsUpgradableTo, NameAndVersion, NamedAndVersioned}; + +pub trait NameAndVersionMap { + type Value; + + fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value>; + fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value>; + + fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError>; + fn num_crates(&self) -> usize; + fn contains_name(&self, name: &str) -> bool { + self.get_versions(name).next().is_some() + } + fn get_versions<'a, 'b>( + &'a self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>; + fn get_versions_mut<'a, 'b>( + &'a mut self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a>; + fn get_version_upgradable_from<T: NamedAndVersioned + IsUpgradableTo>( + &self, + other: &T, + ) -> Option<&NameAndVersion> { + let mut best_version = None; + for (nv, _val) in self.get_versions(other.name()) { + if other.is_upgradable_to(nv) { + best_version.replace(nv); + } + } + best_version + } + fn filter_versions< + 'a: 'b, + 'b, + F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version> + + 'a, + >( + &'a self, + f: F, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a>; +} + +impl<ValueType> NameAndVersionMap for BTreeMap<NameAndVersion, ValueType> { + type Value = ValueType; + + fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value> { + self + } + + fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value> { + self + } + + fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError> { + if self.contains_key(&key) { + Err(CrateError::DuplicateCrateVersion(key.name().to_string(), key.version().clone())) + } else { + self.insert(key, val); + Ok(()) + } + } + + fn num_crates(&self) -> usize { + let mut seen = ::std::collections::HashSet::new(); + for nv in self.keys() { + seen.insert(nv.name().to_string()); + } + seen.len() + } + + fn get_versions<'a, 'b>( + &'a self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> { + let owned_name = name.to_string(); + Box::new( + self.range(std::ops::RangeFrom { + start: NameAndVersion::min_version(name.to_string()), + }) + .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }), + ) + } + + fn get_versions_mut<'a, 'b>( + &'a mut self, + name: &'b str, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a> { + let owned_name = name.to_string(); + Box::new( + self.range_mut(std::ops::RangeFrom { + start: NameAndVersion::min_version(name.to_string()), + }) + .map_while(move |x| if x.0.name() == owned_name { Some(x) } else { None }), + ) + } + + fn filter_versions< + 'a: 'b, + 'b, + F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>) -> HashSet<Version> + + 'a, + >( + &'a self, + f: F, + ) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> { + let mut kept_keys: HashSet<NameAndVersion> = HashSet::new(); + for (key, mut group) in self.iter().group_by(|item| item.0.name()).into_iter() { + kept_keys.extend( + f(&mut group).into_iter().map(move |v| NameAndVersion::new(key.to_string(), v)), + ); + } + Box::new(self.iter().filter(move |(nv, _krate)| kept_keys.contains(*nv))) + } +} + +pub fn crates_with_single_version<'a, ValueType>( + versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, +) -> HashSet<Version> { + let mut vset = HashSet::new(); + versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count(); + if vset.len() != 1 { + vset.clear() + } + vset +} + +pub fn crates_with_multiple_versions<'a, ValueType>( + versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, +) -> HashSet<Version> { + let mut vset = HashSet::new(); + versions.into_iter().map(|(nv, _crate)| vset.insert(nv.version().clone())).count(); + if vset.len() == 1 { + vset.clear() + } + vset +} + +pub fn most_recent_version<'a, ValueType>( + versions: &mut dyn Iterator<Item = (&'a NameAndVersion, &'a ValueType)>, +) -> HashSet<Version> { + let mut vset = HashSet::new(); + if let Some((nv, _crate)) = versions.into_iter().last() { + vset.insert(nv.version().clone()); + } + vset +} + +#[cfg(test)] +pub fn try_name_version_map_from_iter<'a, ValueType>( + nvs: impl IntoIterator<Item = (&'a str, &'a str, ValueType)>, +) -> Result<BTreeMap<NameAndVersion, ValueType>> { + let mut test_map = BTreeMap::new(); + for (name, version, val) in nvs { + test_map.insert_or_error(NameAndVersion::try_from_str(name, version)?, val)?; + } + Ok(test_map) +} + +#[cfg(test)] +mod tests { + use crate::{NameAndVersion, NameAndVersionRef}; + + use super::*; + use anyhow::Result; + use itertools::assert_equal; + + #[test] + fn test_name_and_version_map_empty() -> Result<()> { + let mut test_map: BTreeMap<NameAndVersion, String> = BTreeMap::new(); + let v = Version::parse("1.2.3")?; + let nvp = NameAndVersionRef::new("foo", &v); + // let nvp = NameAndVersion::try_from_str("foo", "1.2.3")?; + assert_eq!(test_map.num_crates(), 0); + assert!(!test_map.contains_key(&nvp as &dyn NamedAndVersioned)); + assert!(!test_map.contains_name("foo")); + assert!(test_map.get(&nvp as &dyn NamedAndVersioned).is_none()); + assert!(test_map.get_mut(&nvp as &dyn NamedAndVersioned).is_none()); + Ok(()) + } + + #[test] + fn test_name_and_version_map_nonempty() -> Result<()> { + let mut test_map = try_name_version_map_from_iter([ + ("foo", "1.2.3", "foo v1".to_string()), + ("foo", "2.3.4", "foo v2".to_string()), + ("bar", "1.0.0", "bar".to_string()), + ])?; + + let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?; + let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?; + let bar = NameAndVersion::try_from_str("bar", "1.0.0")?; + let wrong_name = NameAndVersion::try_from_str("baz", "1.2.3")?; + let wrong_version = NameAndVersion::try_from_str("foo", "1.0.0")?; + + assert_eq!(test_map.num_crates(), 2); + + assert!(test_map.contains_key(&foo1)); + assert!(test_map.contains_key(&foo2)); + assert!(test_map.contains_key(&bar)); + assert!(!test_map.contains_key(&wrong_name)); + assert!(!test_map.contains_key(&wrong_version)); + + assert!(test_map.contains_name("foo")); + assert!(test_map.contains_name("bar")); + assert!(!test_map.contains_name("baz")); + + assert_eq!(test_map.get(&foo1), Some(&"foo v1".to_string())); + assert_eq!(test_map.get(&foo2), Some(&"foo v2".to_string())); + assert_eq!(test_map.get(&bar), Some(&"bar".to_string())); + assert!(test_map.get(&wrong_name).is_none()); + assert!(test_map.get(&wrong_version).is_none()); + + assert_eq!(test_map.get_mut(&foo1), Some(&mut "foo v1".to_string())); + assert_eq!(test_map.get_mut(&foo2), Some(&mut "foo v2".to_string())); + assert_eq!(test_map.get_mut(&bar), Some(&mut "bar".to_string())); + assert!(test_map.get_mut(&wrong_name).is_none()); + assert!(test_map.get_mut(&wrong_version).is_none()); + + assert_eq!( + test_map.get_version_upgradable_from(&NameAndVersion::try_from_str("foo", "1.2.2")?), + Some(&foo1) + ); + + // TOOD: Iter + assert_equal(test_map.keys(), [&bar, &foo1, &foo2]); + + assert_equal(test_map.values(), ["bar", "foo v1", "foo v2"]); + assert_equal(test_map.values_mut(), ["bar", "foo v1", "foo v2"]); + + assert_equal( + test_map.iter().filter(|(_nv, x)| x.starts_with("foo")).map(|(_nv, val)| val), + ["foo v1", "foo v2"], + ); + + test_map.retain(|_nv, x| x.starts_with("foo")); + assert_equal(test_map.values(), ["foo v1", "foo v2"]); + + Ok(()) + } + + #[test] + fn test_filter_versions() -> Result<()> { + let test_map = try_name_version_map_from_iter([ + ("foo", "1.2.3", ()), + ("foo", "2.3.4", ()), + ("bar", "1.0.0", ()), + ])?; + let foo1 = NameAndVersion::try_from_str("foo", "1.2.3")?; + let foo2 = NameAndVersion::try_from_str("foo", "2.3.4")?; + let bar = NameAndVersion::try_from_str("bar", "1.0.0")?; + + assert_equal( + test_map.filter_versions(crates_with_single_version).map(|(nv, _)| nv), + [&bar], + ); + assert_equal( + test_map.filter_versions(crates_with_multiple_versions).map(|(nv, _)| nv), + [&foo1, &foo2], + ); + assert_equal( + test_map.filter_versions(most_recent_version).map(|(nv, _)| nv), + [&bar, &foo2], + ); + + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/pseudo_crate.rs b/tools/external_crates/crate_health/src/pseudo_crate.rs new file mode 100644 index 000000000..55472c0be --- /dev/null +++ b/tools/external_crates/crate_health/src/pseudo_crate.rs @@ -0,0 +1,125 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + fs::{create_dir, write}, + path::{Path, PathBuf}, + process::Command, + str::from_utf8, +}; + +use anyhow::{anyhow, Context, Result}; +use serde::Serialize; +use tinytemplate::TinyTemplate; + +use crate::{ensure_exists_and_empty, NamedAndVersioned}; + +static CARGO_TOML_TEMPLATE: &'static str = include_str!("templates/Cargo.toml.template"); + +#[derive(Serialize)] +struct Dep { + name: String, + version: String, +} + +#[derive(Serialize)] +struct CargoToml { + deps: Vec<Dep>, +} + +pub struct PseudoCrate { + // Absolute path to pseudo-crate. + path: PathBuf, +} + +impl PseudoCrate { + pub fn new<P: Into<PathBuf>>(path: P) -> PseudoCrate { + PseudoCrate { path: path.into() } + } + pub fn init<'a>( + &self, + crates: impl Iterator<Item = &'a (impl NamedAndVersioned + 'a)>, + ) -> Result<()> { + if self.path.exists() { + return Err(anyhow!( + "Can't init pseudo-crate because {} already exists", + self.path.display() + )); + } + ensure_exists_and_empty(&self.path)?; + + let mut deps = Vec::new(); + for krate in crates { + // Special cases: + // * libsqlite3-sys is a sub-crate of rusqlite + // * remove_dir_all has a version not known by crates.io (b/313489216) + if krate.name() != "libsqlite3-sys" { + deps.push(Dep { + name: krate.name().to_string(), + version: if krate.name() == "remove_dir_all" + && krate.version().to_string() == "0.7.1" + { + "0.7.0".to_string() + } else { + krate.version().to_string() + }, + }); + } + } + + let mut tt = TinyTemplate::new(); + tt.add_template("cargo_toml", CARGO_TOML_TEMPLATE)?; + let cargo_toml = self.path.join("Cargo.toml"); + write(&cargo_toml, tt.render("cargo_toml", &CargoToml { deps })?)?; + + create_dir(self.path.join("src")).context("Failed to create src dir")?; + write(self.path.join("src/lib.rs"), "// Nothing").context("Failed to create src/lib.rs")?; + + self.vendor() + + // TODO: Run "cargo deny" + } + pub fn get_path(&self) -> &Path { + self.path.as_path() + } + pub fn add(&self, krate: &impl NamedAndVersioned) -> Result<()> { + let status = Command::new("cargo") + .args(["add", format!("{}@={}", krate.name(), krate.version()).as_str()]) + .current_dir(&self.path) + .spawn() + .context("Failed to spawn 'cargo add'")? + .wait() + .context("Failed to wait on 'cargo add'")?; + if !status.success() { + return Err(anyhow!("Failed to run 'cargo add {}@{}'", krate.name(), krate.version())); + } + Ok(()) + } + pub fn vendor(&self) -> Result<()> { + let output = Command::new("cargo").args(["vendor"]).current_dir(&self.path).output()?; + if !output.status.success() { + return Err(anyhow!( + "cargo vendor failed with exit code {}\nstdout:\n{}\nstderr:\n{}", + output + .status + .code() + .map(|code| { format!("{}", code) }) + .unwrap_or("(unknown)".to_string()), + from_utf8(&output.stdout)?, + from_utf8(&output.stderr)? + )); + } + Ok(()) + } +} diff --git a/tools/external_crates/crate_health/src/reports.rs b/tools/external_crates/crate_health/src/reports.rs new file mode 100644 index 000000000..488bc589f --- /dev/null +++ b/tools/external_crates/crate_health/src/reports.rs @@ -0,0 +1,350 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{fmt::Display, fs::write, path::Path, str::from_utf8}; + +use crate::{ + crates_with_multiple_versions, crates_with_single_version, CompatibleVersionPair, Crate, + CrateCollection, NameAndVersionMap, NamedAndVersioned, VersionMatch, VersionPair, +}; + +use anyhow::Result; +use serde::Serialize; +use tinytemplate::TinyTemplate; + +static SIZE_REPORT_TEMPLATE: &'static str = include_str!("templates/size_report.html.template"); +static TABLE_TEMPLATE: &'static str = include_str!("templates/table.html.template"); + +static CRATE_HEALTH_REPORT_TEMPLATE: &'static str = + include_str!("templates/crate_health.html.template"); +static MIGRATION_REPORT_TEMPLATE: &'static str = + include_str!("templates/migration_report.html.template"); + +pub struct ReportEngine<'template> { + tt: TinyTemplate<'template>, +} + +fn len_formatter(value: &serde_json::Value, out: &mut String) -> tinytemplate::error::Result<()> { + match value { + serde_json::Value::Array(a) => { + out.push_str(&format!("{}", a.len())); + Ok(()) + } + _ => Err(tinytemplate::error::Error::GenericError { + msg: "Can only use length formatter on an array".to_string(), + }), + } +} + +fn linkify(text: &dyn Display, url: &dyn Display) -> String { + format!("<a href=\"{}\">{}</a>", url, text) +} + +impl<'template> ReportEngine<'template> { + pub fn new() -> Result<ReportEngine<'template>> { + let mut tt = TinyTemplate::new(); + tt.add_template("size_report", SIZE_REPORT_TEMPLATE)?; + tt.add_template("table", TABLE_TEMPLATE)?; + tt.add_template("crate_health", CRATE_HEALTH_REPORT_TEMPLATE)?; + tt.add_template("migration", MIGRATION_REPORT_TEMPLATE)?; + tt.add_formatter("len", len_formatter); + Ok(ReportEngine { tt }) + } + pub fn size_report(&self, cc: &CrateCollection) -> Result<String> { + let num_crates = cc.num_crates(); + let crates_with_single_version = cc.filter_versions(&crates_with_single_version).count(); + Ok(self.tt.render( + "size_report", + &SizeReport { + num_crates, + crates_with_single_version, + crates_with_multiple_versions: num_crates - crates_with_single_version, + num_dirs: cc.map_field().len(), + }, + )?) + } + pub fn table<'a>(&self, crates: impl Iterator<Item = &'a Crate>) -> Result<String> { + let mut table = Table::new(&[&"Crate", &"Version", &"Path"]); + for krate in crates { + table.add_row(&[ + &linkify(&krate.name(), &krate.crates_io_url()), + &krate.version().to_string(), + &krate.aosp_url().map_or(format!("{}", krate.relpath().display()), |url| { + linkify(&krate.relpath().display(), &url) + }), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn health_table<'a>(&self, crates: impl Iterator<Item = &'a Crate>) -> Result<String> { + let mut table = Table::new(&[ + &"Crate", + &"Version", + &"Path", + &"Has Android.bp", + &"Generate Android.bp succeeds", + &"Android.bp unchanged", + &"Has cargo_embargo.json", + &"On migration denylist", + ]); + table.set_vertical_headers(); + for krate in crates { + table.add_row(&[ + &linkify(&krate.name(), &krate.crates_io_url()), + &krate.version().to_string(), + &krate.aosp_url().map_or(format!("{}", krate.relpath().display()), |url| { + linkify(&krate.relpath().display(), &url) + }), + &prefer_yes(krate.root().join(krate.android_bp()).exists()), + &prefer_yes_or_summarize( + krate.generate_android_bp_success(), + krate + .generate_android_bp_output() + .map_or("Error".to_string(), |o| { + format!( + "STDOUT:\n{}\n\nSTDERR:\n{}", + from_utf8(&o.stdout).unwrap_or("Error"), + from_utf8(&o.stderr).unwrap_or("Error") + ) + }) + .as_str(), + ), + &prefer_yes_or_summarize( + krate.android_bp_unchanged(), + krate + .android_bp_diff() + .map_or("Error", |o| from_utf8(&o.stdout).unwrap_or("Error")), + ), + &prefer_yes(krate.cargo_embargo_json().exists()), + &prefer_no(krate.is_migration_denied()), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn migratable_table<'a>( + &self, + crate_pairs: impl Iterator<Item = CompatibleVersionPair<'a, Crate>>, + ) -> Result<String> { + let mut table = Table::new(&[&"Crate", &"Old Version", &"New Version", &"Path"]); + for crate_pair in crate_pairs { + let source = crate_pair.source; + let dest = crate_pair.dest; + let dest_version = if source.version() == dest.version() { + "".to_string() + } else { + dest.version().to_string() + }; + table.add_row(&[ + &linkify(&source.name(), &source.crates_io_url()), + &source.version().to_string(), + &dest_version, + &source.aosp_url().map_or(format!("{}", source.relpath().display()), |url| { + linkify(&source.relpath().display(), &url) + }), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn migration_ineligible_table<'a>( + &self, + crates: impl Iterator<Item = &'a Crate>, + ) -> Result<String> { + let mut table = Table::new(&[ + &"Crate", + &"Version", + &"Path", + &"In crates.io", + &"Denylisted", + &"Has Android.bp", + &"Has cargo_embargo.json", + ]); + table.set_vertical_headers(); + for krate in crates { + table.add_row(&[ + &linkify(&krate.name(), &krate.crates_io_url()), + &krate.version().to_string(), + &krate.aosp_url().map_or(format!("{}", krate.relpath().display()), |url| { + linkify(&krate.relpath().display(), &url) + }), + &prefer_yes(krate.is_crates_io()), + &prefer_no(krate.is_migration_denied()), + &prefer_yes(krate.root().join(krate.android_bp()).exists()), + &prefer_yes(krate.cargo_embargo_json().exists()), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn migration_eligible_table<'a>( + &self, + crate_pairs: impl Iterator<Item = VersionPair<'a, Crate>>, + ) -> Result<String> { + let mut table = Table::new(&[ + &"Crate", + &"Version", + &"Path", + &"Compatible version", + &"Patch succeeds", + &"Generate Android.bp succeeds", + &"Android.bp unchanged", + ]); + table.set_vertical_headers(); + for crate_pair in crate_pairs { + let source = crate_pair.source; + let maybe_dest = crate_pair.dest; + table.add_row(&[ + &linkify(&source.name(), &source.crates_io_url()), + &source.version().to_string(), + &source.aosp_url().map_or(format!("{}", source.relpath().display()), |url| { + linkify(&source.relpath().display(), &url) + }), + maybe_dest.map_or(&"None", |dest| { + if dest.version() != source.version() { + dest.version() + } else { + &"" + } + }), + &prefer_yes(!maybe_dest.is_some_and(|dest| !dest.patch_success())), + &prefer_yes_or_summarize( + !maybe_dest.is_some_and(|dest| !dest.generate_android_bp_success()), + maybe_dest + .map_or("Error".to_string(), |dest| { + dest.generate_android_bp_output().map_or("Error".to_string(), |o| { + format!( + "STDOUT:\n{}\n\nSTDERR:\n{}", + from_utf8(&o.stdout).unwrap_or("Error"), + from_utf8(&o.stderr).unwrap_or("Error") + ) + }) + }) + .as_str(), + ), + &prefer_yes_or_summarize( + !maybe_dest.is_some_and(|dest| !dest.android_bp_unchanged()), + maybe_dest.map_or("Error", |dest| { + dest.android_bp_diff() + .map_or("Error", |o| from_utf8(&o.stdout).unwrap_or("Error")) + }), + ), + ]); + } + Ok(self.tt.render("table", &table)?) + } + pub fn health_report( + &self, + cc: &CrateCollection, + output_path: &impl AsRef<Path>, + ) -> Result<()> { + let chr = CrateHealthReport { + crate_count: self.size_report(cc)?, + crate_multiversion: self.table( + cc.filter_versions(&crates_with_multiple_versions).map(|(_nv, krate)| krate), + )?, + healthy: self.table( + cc.map_field() + .iter() + .filter(|(_nv, krate)| krate.is_android_bp_healthy()) + .map(|(_nv, krate)| krate), + )?, + unhealthy: self.health_table( + cc.map_field() + .iter() + .filter(|(_nv, krate)| !krate.is_android_bp_healthy()) + .map(|(_nv, krate)| krate), + )?, + }; + Ok(write(output_path, self.tt.render("crate_health", &chr)?)?) + } + pub fn migration_report( + &self, + m: &VersionMatch<CrateCollection>, + output_path: &impl AsRef<Path>, + ) -> Result<()> { + let mr = MigrationReport { + migratable: self.migratable_table(m.migratable())?, + eligible: self.migration_eligible_table(m.eligible_but_not_migratable())?, + ineligible: self.migration_ineligible_table(m.ineligible())?, + superfluous: self.table(m.superfluous().map(|(_nv, krate)| krate))?, + }; + Ok(write(output_path, self.tt.render("migration", &mr)?)?) + } +} + +pub fn prefer_yes(p: bool) -> &'static str { + if p { + "" + } else { + "No" + } +} +pub fn prefer_yes_or_summarize(p: bool, details: &str) -> String { + if p { + "".to_string() + } else { + format!("<details><summary>No</summary><pre>{}</pre></details>", details) + } +} +pub fn prefer_no(p: bool) -> &'static str { + if p { + "Yes" + } else { + "" + } +} + +#[derive(Serialize)] +pub struct SizeReport { + num_crates: usize, + crates_with_single_version: usize, + crates_with_multiple_versions: usize, + num_dirs: usize, +} + +#[derive(Serialize)] +pub struct Table { + header: Vec<String>, + rows: Vec<Vec<String>>, + vertical: bool, +} + +impl Table { + pub fn new(header: &[&dyn Display]) -> Table { + Table { + header: header.iter().map(|cell| format!("{}", cell)).collect::<Vec<_>>(), + rows: Vec::new(), + vertical: false, + } + } + pub fn add_row(&mut self, row: &[&dyn Display]) { + self.rows.push(row.iter().map(|cell| format!("{}", cell)).collect::<Vec<_>>()); + } + pub fn set_vertical_headers(&mut self) { + self.vertical = true; + } +} + +#[derive(Serialize)] +pub struct CrateHealthReport { + crate_count: String, + crate_multiversion: String, + healthy: String, + unhealthy: String, +} +#[derive(Serialize)] +pub struct MigrationReport { + migratable: String, + eligible: String, + ineligible: String, + superfluous: String, +} diff --git a/tools/external_crates/crate_health/src/templates/Cargo.toml.template b/tools/external_crates/crate_health/src/templates/Cargo.toml.template new file mode 100644 index 000000000..206cfad21 --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/Cargo.toml.template @@ -0,0 +1,10 @@ +[package] +name = "android-pseudo-crate" +version = "0.1.0" +edition = "2021" +publish = false +license = "Apache-2.0" + +[dependencies] +{{ for crate in deps }}{crate.name} = "{crate.version}" +{{ endfor }} diff --git a/tools/external_crates/crate_health/src/templates/crate_health.html.template b/tools/external_crates/crate_health/src/templates/crate_health.html.template new file mode 100644 index 000000000..759fa4c1e --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/crate_health.html.template @@ -0,0 +1,33 @@ +<html> +<head> +<title>Crate health</title> +<style> +table, th, td \{ + border: 1px solid black; + border-collapse: collapse; + padding: 3px; +} +td \{ + vertical-align: top; +} +.vertical \{ + writing-mode: vertical-lr; +} +</style> +</head> +<body> +<h1>Crates in external/rust</h1> +{crate_count | unescaped} +<h1>Crates with multiple versions</h1> +{crate_multiversion | unescaped} +<h1>Healthy crates in external/rust</h1> +<ul> +<li>Has cargo_embargo.json</li> +<li>cargo_embargo runs successfully</li> +<li>The resulting Android.bp is unchanged</li> +</ul> +{healthy | unescaped } +<h1>Unhealthy crates in external/rust</h1> +{unhealthy | unescaped} +</body> +</html>
\ No newline at end of file diff --git a/tools/external_crates/crate_health/src/templates/migration_report.html.template b/tools/external_crates/crate_health/src/templates/migration_report.html.template new file mode 100644 index 000000000..02bae1489 --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/migration_report.html.template @@ -0,0 +1,53 @@ +<html> +<head> +<title>Crate health</title> +<style> +table, th, td \{ + border: 1px solid black; + border-collapse: collapse; + padding: 3px; +} +td \{ + vertical-align: top; +} +.vertical \{ + writing-mode: vertical-lr; +} +</style> +</head> +<body> + +<h1>Migratable crates</h1> +<p>Crates that can be safely migrated: +<ul> +<li>migration-eligible as defined below</li> +<li>Has a vendored crate with a compatible version</li> +<li>Patches apply successfully</li> +<li>cargo_embargo succeeds on the vendored version.</li> +<li>No significant diffs in the resulting Android.bp</li> +</ul> +</p> +{migratable | unescaped} + +<h1>Migration-eligible crates</h1> +<p>Crates that are eligible for migration, but can't yet be migrated: +<ul> +<li>It is in crates.io</li> +<li>It is not denylisted</li> +<li>It has an Android.bp</li> +<li>It has a cargo_embargo.json</li> +</ul> +</p> +{eligible | unescaped} + +<h1>Ineligible crates</h1> +<p>Crates that are not eligible for migration</p> +</p> +{ineligible | unescaped} + +<h1>Superfluous vendored crates</h1> +<p>Vendored crates that we don't know anything about</p> +{superfluous | unescaped} + +</body> +</html>
\ No newline at end of file diff --git a/tools/external_crates/crate_health/src/templates/size_report.html.template b/tools/external_crates/crate_health/src/templates/size_report.html.template new file mode 100644 index 000000000..23f3aec8e --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/size_report.html.template @@ -0,0 +1,6 @@ +<ul> + <li>{num_crates} crates</li> + <li>{crates_with_single_version} have a single version</li> + <li>{crates_with_multiple_versions} have multiple versions</li> + <li>{num_dirs} total crate directories</li> +</ul> diff --git a/tools/external_crates/crate_health/src/templates/table.html.template b/tools/external_crates/crate_health/src/templates/table.html.template new file mode 100644 index 000000000..b36da4761 --- /dev/null +++ b/tools/external_crates/crate_health/src/templates/table.html.template @@ -0,0 +1,10 @@ +<p>{rows | len} crate directories</p> +<table> +<tr>{{ for cell in header}} + <th{{ if vertical }} class="vertical"{{ endif }}>{cell}</th>{{ endfor }} +</tr> +{{ for row in rows }} +<tr>{{ for cell in row }} + <td>{cell | unescaped}</td>{{ endfor }} +</tr>{{ endfor }} +</table>
\ No newline at end of file diff --git a/tools/external_crates/crate_health/src/version_match.rs b/tools/external_crates/crate_health/src/version_match.rs new file mode 100644 index 000000000..49f7ad1ff --- /dev/null +++ b/tools/external_crates/crate_health/src/version_match.rs @@ -0,0 +1,427 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::BTreeMap; + +use anyhow::{anyhow, Result}; + +use crate::{ + generate_android_bps, CrateCollection, Migratable, NameAndVersion, NameAndVersionMap, + NamedAndVersioned, +}; + +#[derive(Debug)] +pub struct VersionPair<'a, T> { + pub source: &'a T, + pub dest: Option<&'a T>, +} + +#[derive(Debug)] +pub struct CompatibleVersionPair<'a, T> { + pub source: &'a T, + pub dest: &'a T, +} + +impl<'a, T> VersionPair<'a, T> { + pub fn to_compatible(self) -> Option<CompatibleVersionPair<'a, T>> { + self.dest.map(|dest| CompatibleVersionPair { source: self.source, dest }) + } +} + +pub struct VersionMatch<CollectionType: NameAndVersionMap> { + source: CollectionType, + dest: CollectionType, + compatibility: BTreeMap<NameAndVersion, Option<NameAndVersion>>, +} + +impl<CollectionType: NameAndVersionMap> VersionMatch<CollectionType> { + pub fn new(source: CollectionType, dest: CollectionType) -> Result<Self> { + let mut vm = VersionMatch { source, dest, compatibility: BTreeMap::new() }; + + for nv in vm.dest.map_field().keys() { + vm.compatibility.insert_or_error(nv.to_owned(), None)?; + } + + for nv in vm.source.map_field().keys() { + let compatibility = if let Some(dest_nv) = vm.dest.get_version_upgradable_from(nv) { + vm.compatibility.map_field_mut().remove(dest_nv).ok_or(anyhow!( + "Destination crate version {} {} expected but not found", + dest_nv.name(), + dest_nv.version() + ))?; + Some(dest_nv.clone()) + } else { + None + }; + vm.compatibility.insert_or_error(nv.to_owned(), compatibility)?; + } + + Ok(vm) + } + pub fn is_superfluous(&self, dest: &dyn NamedAndVersioned) -> bool { + self.dest.map_field().contains_key(dest) + && self.compatibility.get(dest).is_some_and(|compatibility| compatibility.is_none()) + } + pub fn get_compatible_version( + &self, + source: &dyn NamedAndVersioned, + ) -> Option<&NameAndVersion> { + self.compatibility.get(source).and_then(|compatibility| compatibility.as_ref()) + } + pub fn get_compatible_item( + &self, + source: &dyn NamedAndVersioned, + ) -> Option<&CollectionType::Value> { + self.get_compatible_version(source).map(|nv| self.dest.map_field().get(nv).unwrap()) + } + pub fn get_compatible_item_mut( + &mut self, + source: &dyn NamedAndVersioned, + ) -> Option<&mut CollectionType::Value> { + let nv = self.get_compatible_version(source)?.clone(); + self.dest.map_field_mut().get_mut(&nv) + } + + pub fn superfluous(&self) -> impl Iterator<Item = (&NameAndVersion, &CollectionType::Value)> { + self.dest.map_field().iter().filter(|(nv, _val)| { + self.compatibility.get(*nv).is_some_and(|compatibility| compatibility.is_none()) + }) + } + pub fn pairs<'a>(&'a self) -> impl Iterator<Item = VersionPair<'a, CollectionType::Value>> { + self.source + .map_field() + .iter() + .map(|(nv, source)| VersionPair { source, dest: self.get_compatible_item(nv) }) + } + pub fn compatible_pairs<'a>( + &'a self, + ) -> impl Iterator<Item = CompatibleVersionPair<'a, CollectionType::Value>> { + self.pairs().into_iter().filter_map( + |pair: VersionPair<'_, <CollectionType as NameAndVersionMap>::Value>| { + pair.to_compatible() + }, + ) + } + pub fn print(&self) { + for (nv, compatibility) in self.compatibility.iter() { + match compatibility { + Some(dest) => { + println!("{} old {} -> new {}", nv.name(), nv.version(), dest.version()) + } + None => { + if self.dest.contains_name(nv.name()) { + println!("{} {} -> NO MATCHING VERSION", nv.name(), nv.version()) + } else { + println!("{} {} -> NOT FOUND IN NEW", nv.name(), nv.version()) + } + } + } + } + for (nv, _) in self.superfluous() { + println!("{} {} -> NOT FOUND IN OLD", nv.name(), nv.version()); + } + } +} + +impl<CollectionType: NameAndVersionMap> VersionMatch<CollectionType> +where + CollectionType::Value: Migratable, +{ + pub fn ineligible(&self) -> impl Iterator<Item = &CollectionType::Value> { + self.source.map_field().values().filter(|val| !val.is_migration_eligible()) + } + pub fn eligible_but_not_migratable<'a>( + &'a self, + ) -> impl Iterator<Item = VersionPair<'a, CollectionType::Value>> { + self.pairs().filter(|pair| { + pair.source.is_migration_eligible() + && !pair.dest.is_some_and(|dest| dest.is_migratable()) + }) + } + pub fn compatible_and_eligible<'a>( + &'a self, + ) -> impl Iterator<Item = CompatibleVersionPair<'a, CollectionType::Value>> { + self.compatible_pairs().filter(|crate_pair| crate_pair.source.is_migration_eligible()) + } + pub fn migratable<'a>( + &'a self, + ) -> impl Iterator<Item = CompatibleVersionPair<'a, CollectionType::Value>> { + self.compatible_pairs() + .filter(|pair| pair.source.is_migration_eligible() && pair.dest.is_migratable()) + } +} + +impl VersionMatch<CrateCollection> { + pub fn copy_customizations(&self) -> Result<()> { + for pair in self.compatible_and_eligible() { + pair.copy_customizations()?; + } + Ok(()) + } + pub fn stage_crates(&mut self) -> Result<()> { + for pair in self.compatible_and_eligible() { + pair.dest.stage_crate()?; + } + Ok(()) + } + pub fn apply_patches(&mut self) -> Result<()> { + let (s, d, c) = (&self.source, &mut self.dest, &self.compatibility); + for (source_key, source_crate) in s.map_field() { + if source_crate.is_migration_eligible() { + if let Some(dest_crate) = c.get(source_key).and_then(|compatibility| { + compatibility.as_ref().and_then(|dest_key| d.map_field_mut().get_mut(dest_key)) + }) { + dest_crate.apply_patches()? + } + } + } + Ok(()) + } + pub fn generate_android_bps(&mut self) -> Result<()> { + let results = generate_android_bps(self.compatible_and_eligible().map(|pair| pair.dest))?; + for (nv, output) in results.into_iter() { + self.dest + .map_field_mut() + .get_mut(&nv) + .ok_or(anyhow!("Failed to get crate {} {}", nv.name(), nv.version()))? + .set_generate_android_bp_output(output); + } + Ok(()) + } + + pub fn diff_android_bps(&mut self) -> Result<()> { + let mut results = BTreeMap::new(); + for pair in self.compatible_and_eligible() { + results.insert_or_error(NameAndVersion::from(pair.dest), pair.diff_android_bps()?)?; + } + for (nv, output) in results.into_iter() { + self.dest + .map_field_mut() + .get_mut(&nv) + .ok_or(anyhow!("Failed to get crate {} {}", nv.name(), nv.version()))? + .set_diff_output(output); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::try_name_version_map_from_iter; + + use super::*; + use anyhow::Result; + use itertools::assert_equal; + use std::collections::BTreeMap; + + #[test] + fn test_version_map() -> Result<()> { + let source = try_name_version_map_from_iter([ + ("equal", "2.3.4", "equal src".to_string()), + ("compatible", "1.2.3", "compatible src".to_string()), + ("incompatible", "1.1.1", "incompatible src".to_string()), + ("downgrade", "2.2.2", "downgrade src".to_string()), + ("missing", "1.0.0", "missing src".to_string()), + ])?; + let dest = try_name_version_map_from_iter([ + ("equal", "2.3.4", "equal dest".to_string()), + ("compatible", "1.2.4", "compatible dest".to_string()), + ("incompatible", "2.0.0", "incompatible dest".to_string()), + ("downgrade", "2.2.1", "downgrade dest".to_string()), + ("superfluous", "1.0.0", "superfluous dest".to_string()), + ])?; + + let equal = NameAndVersion::try_from_str("equal", "2.3.4")?; + let compatible_old = NameAndVersion::try_from_str("compatible", "1.2.3")?; + let incompatible_old = NameAndVersion::try_from_str("incompatible", "1.1.1")?; + let downgrade_old = NameAndVersion::try_from_str("downgrade", "2.2.2")?; + let missing = NameAndVersion::try_from_str("missing", "1.0.0")?; + + let compatible_new = NameAndVersion::try_from_str("compatible", "1.2.4")?; + let incompatible_new = NameAndVersion::try_from_str("incompatible", "2.0.0")?; + let downgrade_new = NameAndVersion::try_from_str("downgrade", "2.2.1")?; + let superfluous = NameAndVersion::try_from_str("superfluous", "1.0.0")?; + + let mut version_match = VersionMatch::new(source, dest)?; + assert_eq!( + version_match.compatibility, + BTreeMap::from([ + (downgrade_new.clone(), None), + (downgrade_old.clone(), None), + (equal.clone(), Some(equal.clone())), + (compatible_old.clone(), Some(compatible_new.clone())), + (incompatible_old.clone(), None), + (incompatible_new.clone(), None), + (missing.clone(), None), + (superfluous.clone(), None), + ]) + ); + + // assert!(version_match.has_compatible(&equal)); + assert_eq!(version_match.get_compatible_version(&equal), Some(&equal)); + assert_eq!(version_match.get_compatible_item(&equal), Some(&"equal dest".to_string())); + assert_eq!( + version_match.get_compatible_item_mut(&equal), + Some(&mut "equal dest".to_string()) + ); + assert!(!version_match.is_superfluous(&equal)); + + // assert!(version_match.has_compatible(&compatible_old)); + assert_eq!(version_match.get_compatible_version(&compatible_old), Some(&compatible_new)); + assert_eq!( + version_match.get_compatible_item(&compatible_old), + Some(&"compatible dest".to_string()) + ); + assert_eq!( + version_match.get_compatible_item_mut(&compatible_old), + Some(&mut "compatible dest".to_string()) + ); + assert!(!version_match.is_superfluous(&compatible_old)); + assert!(!version_match.is_superfluous(&compatible_new)); + + // assert!(!version_match.has_compatible(&incompatible_old)); + assert!(version_match.get_compatible_version(&incompatible_old).is_none()); + assert!(version_match.get_compatible_item(&incompatible_old).is_none()); + assert!(version_match.get_compatible_item_mut(&incompatible_old).is_none()); + assert!(!version_match.is_superfluous(&incompatible_old)); + assert!(version_match.is_superfluous(&incompatible_new)); + + // assert!(!version_match.has_compatible(&downgrade_old)); + assert!(version_match.get_compatible_version(&downgrade_old).is_none()); + assert!(version_match.get_compatible_item(&downgrade_old).is_none()); + assert!(version_match.get_compatible_item_mut(&downgrade_old).is_none()); + assert!(!version_match.is_superfluous(&downgrade_old)); + assert!(version_match.is_superfluous(&downgrade_new)); + + // assert!(!version_match.has_compatible(&missing)); + assert!(version_match.get_compatible_version(&missing).is_none()); + assert!(version_match.get_compatible_item(&missing).is_none()); + assert!(version_match.get_compatible_item_mut(&missing).is_none()); + assert!(!version_match.is_superfluous(&missing)); + + // assert!(!version_match.has_compatible(&superfluous)); + assert!(version_match.get_compatible_version(&superfluous).is_none()); + assert!(version_match.get_compatible_item(&superfluous).is_none()); + assert!(version_match.get_compatible_item_mut(&superfluous).is_none()); + assert!(version_match.is_superfluous(&superfluous)); + + assert_equal( + version_match.superfluous().map(|(nv, _dest)| nv), + [&downgrade_new, &incompatible_new, &superfluous], + ); + + assert_equal( + version_match.pairs().map(|x| x.source), + ["compatible src", "downgrade src", "equal src", "incompatible src", "missing src"], + ); + assert_equal( + version_match.pairs().map(|x| x.dest), + [ + Some(&"compatible dest".to_string()), + None, + Some(&"equal dest".to_string()), + None, + None, + ], + ); + + assert_equal( + version_match.compatible_pairs().map(|x| x.source), + ["compatible src", "equal src"], + ); + assert_equal( + version_match.compatible_pairs().map(|x| x.dest), + ["compatible dest", "equal dest"], + ); + + Ok(()) + } + + #[derive(Debug, PartialEq, Eq)] + struct FakeMigratable { + name: String, + source: bool, + eligible: bool, + migratable: bool, + } + + impl FakeMigratable { + pub fn source(name: &str, eligible: bool) -> FakeMigratable { + FakeMigratable { name: name.to_string(), source: true, eligible, migratable: false } + } + pub fn dest(migratable: bool) -> FakeMigratable { + FakeMigratable { name: "".to_string(), source: false, eligible: false, migratable } + } + } + + impl Migratable for FakeMigratable { + fn is_migration_eligible(&self) -> bool { + if !self.source { + unreachable!("Checking if dest is migration-eligible"); + } + self.eligible + } + + fn is_migratable(&self) -> bool { + if self.source { + unreachable!("Checking if source is migratable"); + } + self.migratable + } + } + + #[test] + fn test_migratability() -> Result<()> { + let source = try_name_version_map_from_iter([ + ("ineligible", "1.2.3", FakeMigratable::source("ineligible", false)), + ( + "eligible incompatible", + "1.2.3", + FakeMigratable::source("eligible incompatible", true), + ), + ("eligible compatible", "1.2.3", FakeMigratable::source("eligible compatible", true)), + ("migratable", "1.2.3", FakeMigratable::source("migratable", true)), + ( + "migratable incompatible", + "1.2.3", + FakeMigratable::source("migratable incompatible", true), + ), + ])?; + let dest = try_name_version_map_from_iter([ + ("ineligible", "1.2.3", FakeMigratable::dest(true)), + ("eligible incompatible", "2.0.0", FakeMigratable::dest(true)), + ("eligible compatible", "1.2.3", FakeMigratable::dest(false)), + ("migratable", "1.2.3", FakeMigratable::dest(true)), + ("migratable incompatible", "2.0.0", FakeMigratable::dest(true)), + ])?; + + let version_match = VersionMatch::new(source, dest)?; + + assert_equal(version_match.ineligible().map(|m| m.name.as_str()), ["ineligible"]); + assert_equal( + version_match.eligible_but_not_migratable().map(|pair| pair.source.name.as_str()), + ["eligible compatible", "eligible incompatible", "migratable incompatible"], + ); + assert_equal( + version_match.compatible_and_eligible().map(|pair| pair.source.name.as_str()), + ["eligible compatible", "migratable"], + ); + assert_equal( + version_match.migratable().map(|pair| pair.source.name.as_str()), + ["migratable"], + ); + + Ok(()) + } +} diff --git a/tools/external_crates/crate_health_proc_macros/Cargo.toml b/tools/external_crates/crate_health_proc_macros/Cargo.toml new file mode 100644 index 000000000..a62e929d6 --- /dev/null +++ b/tools/external_crates/crate_health_proc_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "crate_health_proc_macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +syn = "2.0" +quote = "1.0" diff --git a/tools/external_crates/crate_health_proc_macros/src/lib.rs b/tools/external_crates/crate_health_proc_macros/src/lib.rs new file mode 100644 index 000000000..a53dd5d65 --- /dev/null +++ b/tools/external_crates/crate_health_proc_macros/src/lib.rs @@ -0,0 +1,124 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::{parse_macro_input, DeriveInput, Error}; + +#[proc_macro_derive(NameAndVersionMap)] +pub fn derive_name_and_version_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + name_and_version_map::expand(input).unwrap_or_else(Error::into_compile_error).into() +} + +mod name_and_version_map { + use proc_macro2::TokenStream; + use quote::quote; + use syn::{ + Data, DataStruct, DeriveInput, Error, Field, GenericArgument, PathArguments, Result, Type, + }; + + pub(crate) fn expand(input: DeriveInput) -> Result<TokenStream> { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let mapfield = get_map_field(get_struct(&input)?)?; + let mapfield_name = mapfield + .ident + .as_ref() + .ok_or(Error::new_spanned(mapfield, "mapfield ident is none"))?; + let (_, value_type) = get_map_type(&mapfield.ty)?; + + let expanded = quote! { + #[automatically_derived] + impl #impl_generics NameAndVersionMap for #name #ty_generics #where_clause { + type Value = #value_type; + + fn map_field(&self) -> &BTreeMap<NameAndVersion, Self::Value> { + self.#mapfield_name.map_field() + } + + fn map_field_mut(&mut self) -> &mut BTreeMap<NameAndVersion, Self::Value> { + self.#mapfield_name.map_field_mut() + } + + fn insert_or_error(&mut self, key: NameAndVersion, val: Self::Value) -> Result<(), CrateError> { + self.#mapfield_name.insert_or_error(key, val) + } + + fn num_crates(&self) -> usize { + self.#mapfield_name.num_crates() + } + + fn get_versions<'a, 'b>(&'a self, name: &'b str) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a Self::Value)> + 'a> { + self.#mapfield_name.get_versions(name) + } + + fn get_versions_mut<'a, 'b>(&'a mut self, name: &'b str) -> Box<dyn Iterator<Item = (&'a NameAndVersion, &'a mut Self::Value)> + 'a> { + self.#mapfield_name.get_versions_mut(name) + } + + fn filter_versions<'a: 'b, 'b, F: Fn(&mut dyn Iterator<Item = (&'b NameAndVersion, &'b Self::Value)>, + ) -> HashSet<Version> + 'a>( + &'a self, + f: F, + ) -> Box<dyn Iterator<Item =(&'a NameAndVersion, &'a Self::Value)> + 'a> { + self.#mapfield_name.filter_versions(f) + } + } + }; + + Ok(TokenStream::from(expanded)) + } + + fn get_struct(input: &DeriveInput) -> Result<&DataStruct> { + match &input.data { + Data::Struct(strukt) => Ok(strukt), + _ => Err(Error::new_spanned(input, "Not a struct")), + } + } + + fn get_map_field(strukt: &DataStruct) -> Result<&Field> { + for field in &strukt.fields { + if let Ok((key_type, _value_type)) = get_map_type(&field.ty) { + if let syn::Type::Path(path) = &key_type { + if path.path.segments.len() == 1 + && path.path.segments[0].ident == "NameAndVersion" + { + return Ok(field); + } + } + } + } + return Err(Error::new_spanned(strukt.struct_token, "No field of type NameAndVersionMap")); + } + + fn get_map_type(typ: &Type) -> Result<(&Type, &Type)> { + if let syn::Type::Path(path) = &typ { + if path.path.segments.len() == 1 && path.path.segments[0].ident == "BTreeMap" { + if let PathArguments::AngleBracketed(args) = &path.path.segments[0].arguments { + if args.args.len() == 2 { + return Ok((get_type(&args.args[0])?, get_type(&args.args[1])?)); + } + } + } + } + Err(Error::new_spanned(typ, "Must be BTreeMap")) + } + + fn get_type(arg: &GenericArgument) -> Result<&Type> { + if let GenericArgument::Type(typ) = arg { + return Ok(typ); + } + Err(Error::new_spanned(arg, "Could not extract argument type")) + } +} diff --git a/treble/compare_bp_system_image.sh b/treble/compare_bp_system_image.sh new file mode 100755 index 000000000..7e8a736bd --- /dev/null +++ b/treble/compare_bp_system_image.sh @@ -0,0 +1,8 @@ +#!/bin/bash +echo "Note: Should run 'lunch aosp_cf_x86_64_only_phone-trunk_staging-userdebug && m aosp_cf_system_x86_64 && m' before running this script" +bp_base_path=$ANDROID_BUILD_TOP/out/soong/.intermediates/device/google/cuttlefish/system_image/aosp_cf_system_x86_64/android_common +latest_hash=$(ls $bp_base_path -t | head -1) +bp_path=$bp_base_path/$latest_hash/root +echo $OUT +echo $bp_path +compare_images -t $OUT $bp_path -s system -i diff --git a/vndk/tools/header-checker/src/repr/protobuf/converter.cpp b/vndk/tools/header-checker/src/repr/protobuf/converter.cpp index c418d80c1..3bf620d47 100644 --- a/vndk/tools/header-checker/src/repr/protobuf/converter.cpp +++ b/vndk/tools/header-checker/src/repr/protobuf/converter.cpp @@ -55,22 +55,22 @@ bool IRDiffToProtobufConverter::AddVTableLayoutDiff( abi_dump:: VTableLayout *new_vtable = vtable_layout_diff_protobuf->mutable_new_vtable(); if (old_vtable == nullptr || new_vtable == nullptr || - !SetIRToProtobufVTableLayout(old_vtable, - vtable_layout_diff_ir->GetOldVTable()) || - !SetIRToProtobufVTableLayout(new_vtable, - vtable_layout_diff_ir->GetNewVTable())) { + !IRToProtobufConverter::ConvertVTableLayoutIR( + old_vtable, vtable_layout_diff_ir->GetOldVTable()) || + !IRToProtobufConverter::ConvertVTableLayoutIR( + new_vtable, vtable_layout_diff_ir->GetNewVTable())) { return false; } return true; } -template <typename T> static bool CopyBaseSpecifiersDiffIRToProtobuf( - google::protobuf::RepeatedPtrField<T> *dst, + google::protobuf::RepeatedPtrField<abi_dump::CXXBaseSpecifier> *dst, const std::vector<CXXBaseSpecifierIR> &bases_ir) { for (auto &&base_ir : bases_ir) { - T *added_base = dst->Add(); - if (!SetIRToProtobufBaseSpecifier(added_base, base_ir)) { + abi_dump::CXXBaseSpecifier *added_base = dst->Add(); + if (!IRToProtobufConverter::ConvertCXXBaseSpecifierIR(added_base, + base_ir)) { return false; } } @@ -102,10 +102,9 @@ bool IRDiffToProtobufConverter::AddRecordFields( } else { field = record_diff_protobuf->add_fields_added(); } - if (field == nullptr) { + if (!IRToProtobufConverter::ConvertRecordFieldIR(field, record_field_ir)) { return false; } - SetIRToProtobufRecordField(field, record_field_ir); } return true; } @@ -126,10 +125,10 @@ bool IRDiffToProtobufConverter::AddRecordFieldDiffs( if (old_field == nullptr || new_field == nullptr) { return false; } - SetIRToProtobufRecordField(old_field, - record_field_diff_ir.GetOldField()); - SetIRToProtobufRecordField(new_field, - record_field_diff_ir.GetNewField()); + IRToProtobufConverter::ConvertRecordFieldIR( + old_field, record_field_diff_ir.GetOldField()); + IRToProtobufConverter::ConvertRecordFieldIR( + new_field, record_field_diff_ir.GetNewField()); } return true; } @@ -203,7 +202,8 @@ static bool AddEnumFields( const std::vector<const EnumFieldIR *> &enum_fields) { for (auto &&enum_field : enum_fields) { abi_dump::EnumFieldDecl *added_enum_field = dst->Add(); - if (!SetIRToProtobufEnumField(added_enum_field, enum_field)) { + if (!IRToProtobufConverter::ConvertEnumFieldIR(added_enum_field, + enum_field)) { return false; } } @@ -218,10 +218,12 @@ static bool AddEnumFieldDiffs( if (field_diff_protobuf == nullptr) { return false; } - if (!SetIRToProtobufEnumField(field_diff_protobuf->mutable_old_field(), - field_diff_ir.GetOldField()) || - !SetIRToProtobufEnumField(field_diff_protobuf->mutable_new_field(), - field_diff_ir.GetNewField())) { + if (!IRToProtobufConverter::ConvertEnumFieldIR( + field_diff_protobuf->mutable_old_field(), + field_diff_ir.GetOldField()) || + !IRToProtobufConverter::ConvertEnumFieldIR( + field_diff_protobuf->mutable_new_field(), + field_diff_ir.GetNewField())) { return false; } } diff --git a/vndk/tools/header-checker/src/repr/protobuf/converter.h b/vndk/tools/header-checker/src/repr/protobuf/converter.h index 191ac0f48..7325951aa 100644 --- a/vndk/tools/header-checker/src/repr/protobuf/converter.h +++ b/vndk/tools/header-checker/src/repr/protobuf/converter.h @@ -20,8 +20,6 @@ #include "repr/protobuf/abi_diff.h" #include "repr/protobuf/abi_dump.h" -#include <llvm/Support/raw_ostream.h> - namespace header_checker { namespace repr { @@ -190,6 +188,7 @@ inline VTableComponentIR::Kind VTableComponentKindProtobufToIR( assert(false); } +// Convert IR to the messages defined in abi_dump.proto. class IRToProtobufConverter { private: static bool AddTemplateInformation( @@ -211,6 +210,21 @@ class IRToProtobufConverter { const EnumTypeIR *enum_ir); public: + static bool ConvertRecordFieldIR( + abi_dump::RecordFieldDecl *record_field_protobuf, + const RecordFieldIR *record_field_ir); + + static bool ConvertCXXBaseSpecifierIR( + abi_dump::CXXBaseSpecifier *base_specifier_protobuf, + const CXXBaseSpecifierIR &base_specifier_ir); + + static bool ConvertVTableLayoutIR( + abi_dump::VTableLayout *vtable_layout_protobuf, + const VTableLayoutIR &vtable_layout_ir); + + static bool ConvertEnumFieldIR(abi_dump::EnumFieldDecl *enum_field_protobuf, + const EnumFieldIR *enum_field_ir); + static abi_dump::EnumType ConvertEnumTypeIR(const EnumTypeIR *enump); static abi_dump::RecordType ConvertRecordTypeIR(const RecordTypeIR *recordp); @@ -257,6 +271,7 @@ class IRToProtobufConverter { const ElfObjectIR *elf_object_ir); }; +// Convert IR to the messages defined in abi_diff.proto. class IRDiffToProtobufConverter { private: static bool AddTypeInfoDiff( @@ -298,76 +313,6 @@ class IRDiffToProtobufConverter { const GlobalVarDiffIR *global_var_diffp); }; -inline void SetIRToProtobufRecordField( - abi_dump::RecordFieldDecl *record_field_protobuf, - const RecordFieldIR *record_field_ir) { - record_field_protobuf->set_field_name(record_field_ir->GetName()); - record_field_protobuf->set_referenced_type( - record_field_ir->GetReferencedType()); - record_field_protobuf->set_access( - AccessIRToProtobuf(record_field_ir->GetAccess())); - record_field_protobuf->set_field_offset(record_field_ir->GetOffset()); - if (record_field_ir->IsBitField()) { - record_field_protobuf->set_is_bit_field(true); - record_field_protobuf->set_bit_width(record_field_ir->GetBitWidth()); - } -} - -inline bool SetIRToProtobufBaseSpecifier( - abi_dump::CXXBaseSpecifier *base_specifier_protobuf, - const CXXBaseSpecifierIR &base_specifier_ir) { - if (!base_specifier_protobuf) { - llvm::errs() << "Protobuf base specifier not valid\n"; - return false; - } - base_specifier_protobuf->set_referenced_type( - base_specifier_ir.GetReferencedType()); - base_specifier_protobuf->set_is_virtual( - base_specifier_ir.IsVirtual()); - base_specifier_protobuf->set_access( - AccessIRToProtobuf(base_specifier_ir.GetAccess())); - return true; -} - -inline bool SetIRToProtobufVTableLayout( - abi_dump::VTableLayout *vtable_layout_protobuf, - const VTableLayoutIR &vtable_layout_ir) { - if (vtable_layout_protobuf == nullptr) { - llvm::errs() << "vtable layout protobuf not valid\n"; - return false; - } - for (auto &&vtable_component_ir : vtable_layout_ir.GetVTableComponents()) { - abi_dump::VTableComponent *added_vtable_component = - vtable_layout_protobuf->add_vtable_components(); - if (!added_vtable_component) { - llvm::errs() << "Couldn't add vtable component\n"; - return false; - } - added_vtable_component->set_kind( - VTableComponentKindIRToProtobuf(vtable_component_ir.GetKind())); - added_vtable_component->set_component_value(vtable_component_ir.GetValue()); - added_vtable_component->set_mangled_component_name( - vtable_component_ir.GetName()); - added_vtable_component->set_is_pure(vtable_component_ir.GetIsPure()); - } - return true; -} - -inline bool SetIRToProtobufEnumField( - abi_dump::EnumFieldDecl *enum_field_protobuf, - const EnumFieldIR *enum_field_ir) { - if (enum_field_protobuf == nullptr) { - return true; - } - enum_field_protobuf->set_name(enum_field_ir->GetName()); - // The "enum_field_value" in the .proto is a signed 64-bit integer. An - // unsigned integer >= (1 << 63) is represented with a negative integer in the - // dump file. Despite the wrong representation, the diff result isn't affected - // because every integer has a unique representation. - enum_field_protobuf->set_enum_field_value(enum_field_ir->GetSignedValue()); - return true; -} - } // namespace repr } // namespace header_checker diff --git a/vndk/tools/header-checker/src/repr/protobuf/ir_dumper.cpp b/vndk/tools/header-checker/src/repr/protobuf/ir_dumper.cpp index 38b2adfa4..317cd773a 100644 --- a/vndk/tools/header-checker/src/repr/protobuf/ir_dumper.cpp +++ b/vndk/tools/header-checker/src/repr/protobuf/ir_dumper.cpp @@ -59,33 +59,88 @@ bool IRToProtobufConverter::AddTypeInfo( return true; } +bool IRToProtobufConverter::ConvertRecordFieldIR( + abi_dump::RecordFieldDecl *record_field_protobuf, + const RecordFieldIR *record_field_ir) { + if (!record_field_protobuf) { + llvm::errs() << "Invalid record field\n"; + return false; + } + record_field_protobuf->set_field_name(record_field_ir->GetName()); + record_field_protobuf->set_referenced_type( + record_field_ir->GetReferencedType()); + record_field_protobuf->set_access( + AccessIRToProtobuf(record_field_ir->GetAccess())); + record_field_protobuf->set_field_offset(record_field_ir->GetOffset()); + if (record_field_ir->IsBitField()) { + record_field_protobuf->set_is_bit_field(true); + record_field_protobuf->set_bit_width(record_field_ir->GetBitWidth()); + } + return true; +} + bool IRToProtobufConverter::AddRecordFields( - abi_dump::RecordType *record_protobuf, - const RecordTypeIR *record_ir) { - // Iterate through the fields and create corresponding ones for the protobuf - // record + abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir) { for (auto &&field_ir : record_ir->GetFields()) { abi_dump::RecordFieldDecl *added_field = record_protobuf->add_fields(); - if (!added_field) { - llvm::errs() << "Couldn't add record field\n"; + if (!IRToProtobufConverter::ConvertRecordFieldIR(added_field, &field_ir)) { + return false; } - SetIRToProtobufRecordField(added_field, &field_ir); } return true; } +bool IRToProtobufConverter::ConvertCXXBaseSpecifierIR( + abi_dump::CXXBaseSpecifier *base_specifier_protobuf, + const CXXBaseSpecifierIR &base_specifier_ir) { + if (!base_specifier_protobuf) { + llvm::errs() << "Protobuf base specifier not valid\n"; + return false; + } + base_specifier_protobuf->set_referenced_type( + base_specifier_ir.GetReferencedType()); + base_specifier_protobuf->set_is_virtual(base_specifier_ir.IsVirtual()); + base_specifier_protobuf->set_access( + AccessIRToProtobuf(base_specifier_ir.GetAccess())); + return true; +} + bool IRToProtobufConverter::AddBaseSpecifiers( abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir) { for (auto &&base_ir : record_ir->GetBases()) { abi_dump::CXXBaseSpecifier *added_base = record_protobuf->add_base_specifiers(); - if (!SetIRToProtobufBaseSpecifier(added_base, base_ir)) { + if (!ConvertCXXBaseSpecifierIR(added_base, base_ir)) { return false; } } return true; } +bool IRToProtobufConverter::ConvertVTableLayoutIR( + abi_dump::VTableLayout *vtable_layout_protobuf, + const VTableLayoutIR &vtable_layout_ir) { + if (vtable_layout_protobuf == nullptr) { + llvm::errs() << "vtable layout protobuf not valid\n"; + return false; + } + for (auto &&vtable_component_ir : vtable_layout_ir.GetVTableComponents()) { + abi_dump::VTableComponent *added_vtable_component = + vtable_layout_protobuf->add_vtable_components(); + if (!added_vtable_component) { + llvm::errs() << "Couldn't add vtable component\n"; + return false; + } + added_vtable_component->set_kind( + VTableComponentKindIRToProtobuf(vtable_component_ir.GetKind())); + added_vtable_component->set_component_value(vtable_component_ir.GetValue()); + added_vtable_component->set_mangled_component_name( + vtable_component_ir.GetName()); + added_vtable_component->set_is_pure(vtable_component_ir.GetIsPure()); + } + return true; +} + bool IRToProtobufConverter::AddVTableLayout( abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir) { @@ -96,7 +151,7 @@ bool IRToProtobufConverter::AddVTableLayout( const VTableLayoutIR &vtable_layout_ir = record_ir->GetVTableLayout(); abi_dump::VTableLayout *vtable_layout_protobuf = record_protobuf->mutable_vtable_layout(); - if (!SetIRToProtobufVTableLayout(vtable_layout_protobuf, vtable_layout_ir)) { + if (!ConvertVTableLayoutIR(vtable_layout_protobuf, vtable_layout_ir)) { return false; } return true; @@ -124,11 +179,12 @@ abi_dump::RecordType IRToProtobufConverter::ConvertRecordTypeIR( return added_record_type; } - abi_dump::ElfObject IRToProtobufConverter::ConvertElfObjectIR( const ElfObjectIR *elf_object_ir) { abi_dump::ElfObject elf_object_protobuf; elf_object_protobuf.set_name(elf_object_ir->GetName()); + elf_object_protobuf.set_binding( + ElfSymbolBindingIRToProtobuf(elf_object_ir->GetBinding())); return elf_object_protobuf; } @@ -136,6 +192,8 @@ abi_dump::ElfFunction IRToProtobufConverter::ConvertElfFunctionIR( const ElfFunctionIR *elf_function_ir) { abi_dump::ElfFunction elf_function_protobuf; elf_function_protobuf.set_name(elf_function_ir->GetName()); + elf_function_protobuf.set_binding( + ElfSymbolBindingIRToProtobuf(elf_function_ir->GetBinding())); return elf_function_protobuf; } @@ -194,11 +252,27 @@ abi_dump::FunctionDecl IRToProtobufConverter::ConvertFunctionIR( return added_function; } +bool IRToProtobufConverter::ConvertEnumFieldIR( + abi_dump::EnumFieldDecl *enum_field_protobuf, + const EnumFieldIR *enum_field_ir) { + if (enum_field_protobuf == nullptr) { + llvm::errs() << "Invalid enum field\n"; + return false; + } + enum_field_protobuf->set_name(enum_field_ir->GetName()); + // The "enum_field_value" in the .proto is a signed 64-bit integer. An + // unsigned integer >= (1 << 63) is represented with a negative integer in the + // dump file. Despite the wrong representation, the diff result isn't affected + // because every integer has a unique representation. + enum_field_protobuf->set_enum_field_value(enum_field_ir->GetSignedValue()); + return true; +} + bool IRToProtobufConverter::AddEnumFields(abi_dump::EnumType *enum_protobuf, const EnumTypeIR *enum_ir) { for (auto &&field : enum_ir->GetFields()) { abi_dump::EnumFieldDecl *enum_fieldp = enum_protobuf->add_enum_fields(); - if (!SetIRToProtobufEnumField(enum_fieldp, &field)) { + if (!ConvertEnumFieldIR(enum_fieldp, &field)) { return false; } } @@ -337,9 +411,7 @@ bool ProtobufIRDumper::AddElfFunctionIR(const ElfFunctionIR *elf_function) { if (!added_elf_function) { return false; } - added_elf_function->set_name(elf_function->GetName()); - added_elf_function->set_binding( - ElfSymbolBindingIRToProtobuf(elf_function->GetBinding())); + *added_elf_function = ConvertElfFunctionIR(elf_function); return true; } @@ -348,9 +420,7 @@ bool ProtobufIRDumper::AddElfObjectIR(const ElfObjectIR *elf_object) { if (!added_elf_object) { return false; } - added_elf_object->set_name(elf_object->GetName()); - added_elf_object->set_binding( - ElfSymbolBindingIRToProtobuf(elf_object->GetBinding())); + *added_elf_object = ConvertElfObjectIR(elf_object); return true; } diff --git a/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp b/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp index 773571460..47c4f3761 100644 --- a/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp +++ b/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp @@ -22,6 +22,7 @@ #include <memory> #include <google/protobuf/text_format.h> +#include <llvm/Support/raw_ostream.h> namespace header_checker { |