129 строки
4.1 KiB
Bash
Executable File
129 строки
4.1 KiB
Bash
Executable File
#!/bin/bash -e
|
|
|
|
# This script takes a csproj as input and creates a file with a make variable
|
|
# that lists the input files for that csproj.
|
|
#
|
|
# The idea is to use this make variable as a dependency to a make rule that
|
|
# builds the csproj.
|
|
#
|
|
# The advantage of using make to determine if a csproj needs to be rebuilt is
|
|
# that it's much faster when nothing needs to be done: make takes ~0.015s to
|
|
# figure this out, msbuild takes ~1.5s. Multiply by the number of csprojs
|
|
# (currently 3: mtouch, mmp, generator) by the number of times built every day
|
|
# (dozens), and seconds adds up as years pass.
|
|
#
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
if test -z "$1"; then
|
|
echo "Must specify the project file to process."
|
|
exit 1
|
|
fi
|
|
|
|
# Input: the csproj file to process.
|
|
# Output: /path/to/proj.csproj.inc
|
|
|
|
PROJECT_FILE="$1"
|
|
PROJECT=$(basename -s .csproj "$PROJECT_FILE")
|
|
PROJECT_DIR=$(dirname "$PROJECT_FILE")
|
|
FRAGMENT_PATH="$2"
|
|
REFERENCES_PATH=$(pwd)/$PROJECT-references.txt
|
|
|
|
if test -z "$FRAGMENT_PATH"; then
|
|
FRAGMENT_PATH=$PROJECT_FILE.inc
|
|
fi
|
|
|
|
if test -z "$BUILD_EXECUTABLE"; then
|
|
BUILD_EXECUTABLE=msbuild
|
|
fi
|
|
|
|
if test -z "$BUILD_VERBOSITY"; then
|
|
BUILD_VERBOSITY=/verbosity:diag
|
|
fi
|
|
|
|
# ProjectInspector.csproj is an MSBuild file with a target
|
|
# (WriteProjectReferences) that takes another project file as input (the
|
|
# ProjectFile variable) and writes all the project references (recursively) to
|
|
# a file (the ReferenceListPath variable).
|
|
(
|
|
cp ProjectInspector.csproj "$PROJECT_DIR"
|
|
cd "$PROJECT_DIR"
|
|
$BUILD_EXECUTABLE ProjectInspector.csproj "/t:WriteProjectReferences" "/p:ProjectFile=$PROJECT_FILE" "/p:ReferenceListPath=$REFERENCES_PATH" $BUILD_VERBOSITY /nologo
|
|
rm -f ProjectInspector.csproj
|
|
)
|
|
|
|
# Now we have a list of all the project referenced by the input project. The
|
|
# ProjectInspector.csproj also has a target (WriteInputs) that can list all
|
|
# the inputs for a project.
|
|
#
|
|
# The caveat here is that ProjectInspector.csproj must be in the same
|
|
# directory as the inspected project, because relative paths in the inspected
|
|
# project are evaluated according to the location of the main project file
|
|
# (our ProjectInspector.csproj). Relative paths can't be fixed manually
|
|
# afterwards, because relative paths with patterns (where MSBuild evaluates
|
|
# the patterns) won't come out right.
|
|
#
|
|
# So we copy ProjectInspector.csproj to the inspected project's directory and
|
|
# execute it from there.
|
|
|
|
TMPPROJ=
|
|
|
|
# Make sure we don't leave temporary files around
|
|
function delete_tmpproj
|
|
{
|
|
rm -f "$REFERENCES_PATH"
|
|
if test -n "$TMPPROJ"; then
|
|
rm -f "$TMPPROJ"
|
|
fi
|
|
}
|
|
trap delete_tmpproj EXIT
|
|
trap delete_tmpproj ERR
|
|
|
|
echo "$PROJECT_FILE" >> "$REFERENCES_PATH"
|
|
|
|
INPUT_PATHS=()
|
|
for proj in $(sort "$REFERENCES_PATH" | uniq); do
|
|
TMPPROJ=$PROJECT_DIR/ProjectInspector.csproj
|
|
# Copy ProjectInspector.csproj to target directory.
|
|
cp ProjectInspector.csproj "$TMPPROJ"
|
|
|
|
# Execute the WriteInputs target
|
|
proj_name=$(basename "$proj")
|
|
proj_dir=$(dirname "$proj")
|
|
inputs_path=$PWD/$proj_name.inputs
|
|
INPUT_PATHS+=("$inputs_path")
|
|
|
|
(
|
|
cd "$(dirname "$proj")"
|
|
$BUILD_EXECUTABLE "$TMPPROJ" "/t:WriteInputs" "/p:ProjectFile=$proj" "/p:InputsPath=$inputs_path" $BUILD_VERBOSITY /nologo
|
|
)
|
|
|
|
# The output contains relative paths, relative to the csproj directory
|
|
# Change those to full paths by prepending the csproj directory.
|
|
sed -i '' "s@^@$proj_dir/@" "$inputs_path"
|
|
|
|
# Change to Make syntax. This is horrifically difficult in MSBuild,
|
|
# because MSBuild blindly replaces backslashes with forward slashes (i.e.
|
|
# windows paths to unix paths...)
|
|
sed -i '' "s_^\\(.*\\)\$_ \\1 \\\\_" "$inputs_path"
|
|
|
|
# Clean up
|
|
rm -f "$TMPPROJ"
|
|
done
|
|
|
|
# Create the csproj.inc with the Makefile variable
|
|
|
|
# First add a dependency for the csproj.inc so that it's rebuilt if the project itself changes.
|
|
echo "${PROJECT}.csproj.inc: ${PROJECT_FILE} $PWD/ProjectInspector.csproj" > "$FRAGMENT_PATH"
|
|
# Now create the variable with all the input files.
|
|
echo "${PROJECT//-/_}_dependencies = \\" >> "$FRAGMENT_PATH"
|
|
sort "${INPUT_PATHS[@]}" | uniq >> "$FRAGMENT_PATH"
|
|
|
|
# Simplify paths somewhat by removing the current directory
|
|
if test -z "$ABSOLUTE_PATHS"; then
|
|
sed -i '' "s@$PROJECT_DIR/@@" "$FRAGMENT_PATH"
|
|
fi
|
|
|
|
# Cleanup
|
|
rm -f "${INPUT_PATHS[@]}"
|