| #!/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 <git treeish> | |
| $0 <path>:<git treeish> <path>:<git treeish> | |
| 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 <git treeish>' | |
| 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.zip" | |
| unzipInPlace "${tempOutPath}/dist/top-of-tree-m2repository-all.zip" | |
| unzipInPlace "${tempOutPath}/dist/docs-tip-of-tree.zip" | |
| unzipInPlace "${tempOutPath}/dist/docs-public.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 |