#!/bin/bash # # Copyright (C) 2019 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. # set -e supportRoot="$(cd $(dirname $0)/.. && pwd)" checkoutRoot="$(cd ${supportRoot}/../.. && pwd)" function die() { echo "$@" >&2 exit 1 } function usage() { violation="$1" die " Usage: $0 $0 : : Validates that libraries built from the given versions are the same as the build outputs built at HEAD. This can be used to validate that a refactor did not change the outputs. If a git treeish is given with no path, the path is considered to be frameworks/support Example: $0 HEAD^ Example: $0 prebuilts/androidx/external:HEAD^ frameworks/support:work^ * A git treeish is what you type when you run 'git checkout ' See also https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftree-ishatree-ishalsotreeish . You can also supply additional arguments that will be passed through to validateRefactorHelper.py, using -P For example, the baseline arguments that validateRefactorHelper.py accepts. Example: $0 HEAD^ -p agpKmp validateRefactor also accepts git treeishes as named arguments using -g Example: $0 -g HEAD^ -p agpKmp " return 1 } # Fills in a default repository path of "frameworks/support:" for any args that don't specify # their repository. Given an input of: "work^ prebuilts/androidx/external:HEAD^", should return # "frameworks/support:work^ prebuilts/androidx/external:HEAD^". function expandCommitArgs() { inputSpecs="$@" outputSpecs="" for spec in $inputSpecs; do if echo "$spec" | grep -v ":" >/dev/null; then spec="frameworks/support:$spec" fi outputSpecs="$outputSpecs $spec" done echo $outputSpecs } # Given a list of paths like "frameworks/support prebuilts/androidx/external", # runs `git checkout -` in each function uncheckout() { repositoryPaths="$@" for repositoryPath in $repositoryPaths; do echoAndDo git -C "$checkoutRoot/$repositoryPath" checkout - done } # Given a list of version specs like "a/b:c d/e:f", returns just the paths: "a/b d/e" function getParticipatingProjectPaths() { specs="$@" result="" for arg in $specs; do echo parsing $arg >&2 repositoryPath="$(echo $arg | sed 's|\([^:]*\):\([^:]*\)|\1|')" otherVersion="$(echo $arg | sed 's|\([^:]*\):\([^:]*\)|\2|')" if [ "$otherVersion" != "HEAD" ]; then result="$result $repositoryPath" fi done echo $result } # Given a list of paths, returns a string containing the currently checked-out version of each function getCurrentCommits() { repositoryPaths="$@" result="" for repositoryPath in $repositoryPaths; do currentVersion="$(cd $checkoutRoot/$repositoryPath && git log -1 --format=%H)" result="$result $repositoryPath:$currentVersion" done echo $result } function echoAndDo() { echo "$*" eval "$*" } # Given a list of version specs like "a/b:c d/e:f", checks out the appropriate version in each # In this example it would be `cd a/b && git checkout e` and `cd e/e && git checkout f` function checkout() { versionSpecs="$1" for versionSpec in $versionSpecs; do project="$(echo $versionSpec | sed 's|\([^:]*\):\([^:]*\)|\1|')" ref="$(echo $versionSpec | sed 's|\([^:]*\):\([^:]*\)|\2|')" echo "checking out $ref in project $project" echoAndDo git -C "$checkoutRoot/$project" checkout "$ref" done } function unzipInPlace() { archiveName="$1" echoAndDo unzip -q "$archiveName" -d "${archiveName}.unzipped" } function doBuild() { # build androidx echoAndDo ./gradlew createAllArchives zipDocs --no-daemon --rerun-tasks --offline -Pandroidx.highMemory -Pandroidx.constraints=true archiveName="top-of-tree-m2repository-all-0.zip" unzipInPlace "${tempOutPath}/dist/top-of-tree-m2repository-all-0.zip" unzipInPlace "${tempOutPath}/dist/docs-tip-of-tree-0.zip" unzipInPlace "${tempOutPath}/dist/docs-public-0.zip" } nonNamedArgs=() oldCommits=() passThruArgs=() while [ $OPTIND -le "$#" ]; do if getopts ":p:g:" opt; then case $opt in \? ) usage;; g ) oldCommits+="$(expandCommitArgs $OPTARG)";; p ) passThruArgs+="$OPTARG";; esac case $OPTARG in -*) usage;; esac else nonNamedArgs+=("${!OPTIND}") ((OPTIND++)) fi done oldCommits+="$(expandCommitArgs $nonNamedArgs)" projectPaths="$(getParticipatingProjectPaths $oldCommits)" if [ "$oldCommits" == "" ]; then usage fi newCommits="$(getCurrentCommits $projectPaths)" cd "$supportRoot" if [[ $(git update-index --refresh) ]]; then echo "You have local changes; stash or commit them or this script won't work"; exit 1; fi if [[ $(git diff-index --quiet HEAD) ]]; then echo "You have local changes; stash or commit them or this script won't work"; exit 1; fi echo old commits: $oldCommits echo new commits: $newCommits cd "$supportRoot" oldOutPath="${checkoutRoot}/out-old" newOutPath="${checkoutRoot}/out-new" tempOutPath="${checkoutRoot}/out" rm -rf "$oldOutPath" "$newOutPath" "$tempOutPath" echo building new commit doBuild mv "$tempOutPath" "$newOutPath" echo building previous commit checkout "$oldCommits" if doBuild; then echo previous build succeeded else echo previous build failed uncheckout "$projectPaths" exit 1 fi uncheckout "$projectPaths" mv "$tempOutPath" "$oldOutPath" echo echo diffing results # This script performs the diff, and filters out known issues and non-issues with baselines python3 development/validateRefactorHelper.py "$passThruArgs" echo end of difference