Imported existing code
This commit is contained in:
458
scripts/automated_build_scripts/opensrc-build.sh
Executable file
458
scripts/automated_build_scripts/opensrc-build.sh
Executable file
@@ -0,0 +1,458 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This is the build script used by Linden Lab's autmated build system.
|
||||
#
|
||||
|
||||
set -x
|
||||
|
||||
export INSTALL_USE_HTTP_FOR_SCP=true
|
||||
export PATH=/bin:/usr/bin:$PATH
|
||||
arch=`uname | cut -b-6`
|
||||
here=`echo $0 | sed 's:[^/]*$:.:'`
|
||||
year=`date +%Y`
|
||||
branch=`svn info | grep '^URL:' | sed 's:.*/::'`
|
||||
revision=`svn info | grep '^Revision:' | sed 's/.*: //'`
|
||||
top=`cd "$here/../../.." && pwd`
|
||||
|
||||
[ x"$WGET_CACHE" = x ] && export WGET_CACHE=/var/tmp/parabuild/wget
|
||||
[ x"$S3GET_URL" = x ] && export S3GET_URL=http://viewer-source-downloads.s3.amazonaws.com/$year
|
||||
[ x"$S3PUT_URL" = x ] && export S3PUT_URL=https://s3.amazonaws.com/viewer-source-downloads/$year
|
||||
[ x"$S3SYMBOL_URL" = x ] && export S3SYMBOL_URL=https://s3.amazonaws.com/automated-builds-secondlife-com/binaries
|
||||
[ x"$PUBLIC_URL" = x ] && export PUBLIC_URL=http://secondlife.com/developers/opensource/downloads/$year
|
||||
[ x"$PUBLIC_EMAIL" = x ] && export PUBLIC_EMAIL=sldev-commits@lists.secondlife.com
|
||||
# Make sure command worked and bail out if not, reporting failure to parabuild
|
||||
fail()
|
||||
{
|
||||
release_lock
|
||||
echo "BUILD FAILED" $@
|
||||
exit 1
|
||||
}
|
||||
|
||||
pass()
|
||||
{
|
||||
release_lock
|
||||
echo "BUILD SUCCESSFUL"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Locking to avoid contention with u-s-c
|
||||
LOCK_CREATE=/usr/bin/lockfile-create
|
||||
LOCK_TOUCH=/usr/bin/lockfile-touch
|
||||
LOCK_REMOVE=/usr/bin/lockfile-remove
|
||||
LOCK_PROCESS=
|
||||
|
||||
locking_available()
|
||||
{
|
||||
test -x "$LOCK_CREATE"\
|
||||
-a -x "$LOCK_TOUCH"\
|
||||
-a -x "$LOCK_REMOVE"
|
||||
}
|
||||
|
||||
acquire_lock()
|
||||
{
|
||||
if locking_available
|
||||
then
|
||||
if "$LOCK_CREATE" /var/lock/update-system-config --retry 99
|
||||
then
|
||||
"$LOCK_TOUCH" /var/lock/update-system-config &
|
||||
LOCK_PROCESS="$!"
|
||||
else
|
||||
fail acquire lock
|
||||
fi
|
||||
else
|
||||
true
|
||||
fi
|
||||
}
|
||||
|
||||
release_lock()
|
||||
{
|
||||
if locking_available
|
||||
then
|
||||
if test x"$LOCK_PROCESS" != x
|
||||
then
|
||||
kill "$LOCK_PROCESS"
|
||||
"$LOCK_REMOVE" /var/lock/update-system-config
|
||||
else
|
||||
echo No Lock Acquired >&2
|
||||
fi
|
||||
else
|
||||
true
|
||||
fi
|
||||
}
|
||||
|
||||
get_asset()
|
||||
{
|
||||
mkdir -p "$WGET_CACHE" || fail creating WGET_CACHE
|
||||
local tarball=`basename "$1"`
|
||||
test -r "$WGET_CACHE/$tarball" || ( cd "$WGET_CACHE" && curl --location --remote-name "$1" || fail getting $1 )
|
||||
case "$tarball" in
|
||||
*.zip) unzip -qq -d "$top" -o "$WGET_CACHE/$tarball" || fail unzip $tarball ;;
|
||||
*.tar.gz|*.tgz) tar -C "$top" -xzf "$WGET_CACHE/$tarball" || fail untar $tarball ;;
|
||||
*) fail unrecognized filetype: $tarball ;;
|
||||
esac
|
||||
}
|
||||
|
||||
s3_available()
|
||||
{
|
||||
test -x "$helpers/s3get.sh" -a -x "$helpers/s3put.sh" -a -r "$helpers/s3curl.pl"
|
||||
}
|
||||
|
||||
build_dir_Darwin()
|
||||
{
|
||||
echo build-darwin-universal
|
||||
}
|
||||
|
||||
build_dir_Linux()
|
||||
{
|
||||
echo viewer-linux-i686-`echo $1 | tr A-Z a-z`
|
||||
}
|
||||
|
||||
build_dir_CYGWIN()
|
||||
{
|
||||
echo build-vc80
|
||||
}
|
||||
|
||||
installer_Darwin()
|
||||
{
|
||||
ls -1td "`build_dir_Darwin Release`/newview/"*.dmg 2>/dev/null | sed 1q
|
||||
}
|
||||
|
||||
installer_Linux()
|
||||
{
|
||||
ls -1td "`build_dir_Linux Release`/newview/"*.tar.bz2 2>/dev/null | sed 1q
|
||||
}
|
||||
|
||||
installer_CYGWIN()
|
||||
{
|
||||
d=`build_dir_CYGWIN Release`
|
||||
p=`sed 's:.*=::' "$d/newview/Release/touched.bat"`
|
||||
echo "$d/newview/Release/$p"
|
||||
}
|
||||
|
||||
# deal with aborts etc..
|
||||
trap fail 1 2 3 14 15
|
||||
|
||||
# Check location
|
||||
cd "$here/../.."
|
||||
|
||||
test -x ../linden/scripts/automated_build_scripts/opensrc-build.sh\
|
||||
|| fail 'The parent dir of your checkout needs to be named "linden"'
|
||||
|
||||
. doc/asset_urls.txt
|
||||
get_asset "$SLASSET_ART"
|
||||
|
||||
update_version_files=
|
||||
|
||||
# Set up platform specific stuff
|
||||
case "$arch" in
|
||||
|
||||
# Note that we can only build the "Release" variant for Darwin, because of a compiler bug:
|
||||
# ld: bl out of range (-16777272 max is +/-16M)
|
||||
# from __static_initialization_and_destruction_0(int, int)at 0x033D319C
|
||||
# in __StaticInit of
|
||||
# indra/build-darwin-universal/newview/SecondLife.build/Debug/Second Life.build/Objects-normal/ppc/llvoicevisualizer.o
|
||||
# to ___cxa_atexit$island_2 at 0x023D50F8
|
||||
# in __text of
|
||||
# indra/build-darwin-universal/newview/SecondLife.build/Debug/Second Life.build/Objects-normal/ppc/Second Life
|
||||
# in __static_initialization_and_destruction_0(int, int)
|
||||
# from indra/build-darwin-universal/newview/SecondLife.build/Debug/Second Life.build/Objects-normal/ppc/llvoicevisualizer.o
|
||||
|
||||
Darwin)
|
||||
helpers=/usr/local/buildscripts/shared/latest
|
||||
variants="Release"
|
||||
cmake_generator="Xcode"
|
||||
fmod=fmodapi375mac
|
||||
fmod_tar="$fmod.zip"
|
||||
fmod_so=libfmod.a
|
||||
fmod_lib=lib
|
||||
target_dirs="libraries/universal-darwin/lib_debug
|
||||
libraries/universal-darwin/lib_release
|
||||
libraries/universal-darwin/lib_release_client"
|
||||
other_archs="$S3GET_URL/$branch/$revision/CYGWIN $S3GET_URL/$branch/$revision/Linux"
|
||||
symbolfiles=
|
||||
mail="$helpers"/mail.py
|
||||
all_done="$helpers"/all_done.py
|
||||
test -r "$helpers/update_version_files.py" && update_version_files="$helpers/update_version_files.py"
|
||||
libs_asset="$SLASSET_LIBS_DARWIN"
|
||||
;;
|
||||
|
||||
CYGWIN)
|
||||
helpers=/cygdrive/c/buildscripts/shared/latest
|
||||
variants="Debug RelWithDebInfo Release"
|
||||
#variants="Release"
|
||||
cmake_generator="vc80"
|
||||
fmod=fmodapi375win
|
||||
fmod_tar=fmodapi375win.zip
|
||||
fmod_so=fmodvc.lib
|
||||
fmod_lib=lib
|
||||
target_dirs="libraries/i686-win32/lib/debug
|
||||
libraries/i686-win32/lib/release"
|
||||
other_archs="$S3GET_URL/$branch/$revision/Darwin $S3GET_URL/$branch/$revision/Linux"
|
||||
symbolfiles="newview/Release/secondlife-bin.pdb newview/Release/secondlife-bin.map newview/Release/secondlife-bin.exe"
|
||||
export PATH="/cygdrive/c/Python25:/cygdrive/c/Program Files/Cmake 2.6/bin":$PATH
|
||||
export PERL="/cygdrive/c/Perl/bin/perl.exe"
|
||||
export S3CURL="C:\\buildscripts\\shared\\latest\\hg\\bin\\s3curl.py"
|
||||
export SIGN_PY="C:\\buildscripts\\shared\\latest\\code-signing\\sign.py"
|
||||
export CURL="C:\\cygwin\\bin\\curl.exe"
|
||||
mail="C:\\buildscripts\\shared\\latest\\mail.py"
|
||||
all_done="C:\\buildscripts\\shared\\latest\\all_done.py"
|
||||
test -r "$helpers/update_version_files.py" && update_version_files="C:\\buildscripts\\shared\\latest\\update_version_files.py"
|
||||
libs_asset="$SLASSET_LIBS_WIN32"
|
||||
;;
|
||||
|
||||
Linux)
|
||||
helpers=/var/opt/parabuild/buildscripts/shared/latest
|
||||
if [ x"$CXX" = x ]
|
||||
then
|
||||
if test -x /usr/bin/g++-4.1
|
||||
then
|
||||
if test -x /usr/bin/distcc
|
||||
then
|
||||
export CXX="/usr/bin/distcc /usr/bin/g++-4.1"
|
||||
else
|
||||
export CXX=/usr/bin/g++-4.1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
acquire_lock
|
||||
variants="Debug RelWithDebInfo Release"
|
||||
#variants="Release"
|
||||
cmake_generator="Unix Makefiles"
|
||||
fmod=fmodapi375linux
|
||||
fmod_tar="$fmod".tar.gz
|
||||
fmod_so=libfmod-3.75.so
|
||||
fmod_lib=.
|
||||
target_dirs="libraries/i686-linux/lib_debug
|
||||
libraries/i686-linux/lib_release
|
||||
libraries/i686-linux/lib_release_client"
|
||||
other_archs="$S3GET_URL/$branch/$revision/Darwin $S3GET_URL/$branch/$revision/CYGWIN"
|
||||
symbolfiles=
|
||||
mail="$helpers"/mail.py
|
||||
all_done="$helpers"/all_done.py
|
||||
test -r "$helpers/update_version_files.py" && update_version_files="$helpers/update_version_files.py"
|
||||
# Change the DISTCC_DIR to be somewhere that the parabuild process can write to
|
||||
if test -r /etc/debian_version
|
||||
then
|
||||
[ x"$DISTCC_DIR" = x ] && export DISTCC_DIR=/var/tmp/parabuild
|
||||
#case `cat /etc/debian_version` in
|
||||
#3.*) [ x"$DISTCC_HOSTS" = x ]\
|
||||
# && export DISTCC_HOSTS="build-linux-1/3
|
||||
# station30/2,lzo" ;;
|
||||
#4.*) [ x"$DISTCC_HOSTS" = x ]\
|
||||
# && export DISTCC_HOSTS="build-linux-6/2,lzo
|
||||
# build-linux-2/2,lzo
|
||||
# build-linux-3/2,lzo
|
||||
# build-linux-4/2,lzo
|
||||
# build-linux-5/2,lzo
|
||||
# build-linux-7/2,lzo
|
||||
# build-linux-8/2,lzo
|
||||
# build-linux-9/2,lzo" ;;
|
||||
#esac
|
||||
# Temp fix for Linux so that parabuild passes: use the new Linux build farm
|
||||
export hostname=`hostname -f`
|
||||
export phx_DISTCC_HOSTS="build-linux0.phx.lindenlab.com/2 build-linux1.phx.lindenlab.com/2 build-linux2.phx.lindenlab.com/2 build-linux3.phx.lindenlab.com/2 build-linux5.phx.lindenlab.com/2 build-linux5.phx.lindenlab.com/2 build-linux6.phx.lindenlab.com/2 "
|
||||
export dfw_DISTCC_HOSTS="build-linux7.dfw.lindenlab.com/2 build-linux8.dfw.lindenlab.com/2 build-linux9.dfw.lindenlab.com/2 build-linux10.dfw.lindenlab.com/2 build-linux11.dfw.lindenlab.com/2 build-linux12.dfw.lindenlab.com/2 build-linux13.dfw.lindenlab.com/2 build-linux14.dfw.lindenlab.com/2 build-linux15.dfw.lindenlab.com/2"
|
||||
case "$hostname" in
|
||||
*.dfw.*) export DISTCC_HOSTS="$dfw_DISTCC_HOSTS" ;;
|
||||
*.phx.*) export DISTCC_HOSTS="$phx_DISTCC_HOSTS" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
libs_asset="$SLASSET_LIBS_LINUXI386"
|
||||
;;
|
||||
|
||||
*) fail undefined $arch ;;
|
||||
esac
|
||||
|
||||
get_asset "http://www.fmod.org/index.php/release/version/$fmod_tar"
|
||||
|
||||
case "$arch" in
|
||||
|
||||
Darwin)
|
||||
# Create fat binary on Mac...
|
||||
if lipo -create -output "../$fmod"/api/$fmod_lib/libfmod-universal.a\
|
||||
"../$fmod"/api/$fmod_lib/libfmod.a\
|
||||
"../$fmod"/api/$fmod_lib/libfmodx86.a
|
||||
then
|
||||
mv "../$fmod"/api/$fmod_lib/libfmod.a "../$fmod"/api/$fmod_lib/libfmodppc.a
|
||||
mv "../$fmod"/api/$fmod_lib/libfmod-universal.a "../$fmod"/api/$fmod_lib/libfmod.a
|
||||
echo Created fat binary
|
||||
else
|
||||
fail running lipo
|
||||
fi
|
||||
;;
|
||||
|
||||
CYGWIN)
|
||||
# install Quicktime. This will fail outside of Linden's network
|
||||
scripts/install.py quicktime
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# Only run this if the script exists
|
||||
if test x"$update_version_files" = x
|
||||
then
|
||||
echo "Private Build..." > indra/build.log
|
||||
[ x"$VIEWER_CHANNEL" = x ] && export VIEWER_CHANNEL="CommunityDeveloper"
|
||||
else
|
||||
# By right, this should be in the branched source tree, but for now it will be a helper
|
||||
python "$update_version_files" --verbose --src-root=. --viewer > indra/build.log
|
||||
[ x"$VIEWER_CHANNEL" = x ] && export VIEWER_CHANNEL="Snowglobe Test Build"
|
||||
fi
|
||||
|
||||
# First, go into the directory where the code was checked out by Parabuild
|
||||
cd indra
|
||||
|
||||
# This is the way it works now, but it will soon work on a variant dependent way
|
||||
for target_dir in $target_dirs
|
||||
do
|
||||
mkdir -p "../$target_dir"
|
||||
cp -f "../../$fmod/api/$fmod_lib/$fmod_so" "../$target_dir"
|
||||
done
|
||||
mkdir -p "../libraries/include"
|
||||
cp -f "../../$fmod/api/inc/"* "../libraries/include"
|
||||
|
||||
# Special Windows case
|
||||
test -r "../../$fmod/api/fmod.dll" && cp -f "../../$fmod/api/fmod.dll" newview
|
||||
|
||||
# Now run the build command over all variants
|
||||
succeeded=true
|
||||
|
||||
### TEST CODE - remove when done
|
||||
### variants=
|
||||
### echo "Artificial build failure to test notifications" > build.log
|
||||
### succeeded=false
|
||||
### END TEST CODE
|
||||
|
||||
for variant in $variants
|
||||
do
|
||||
build_dir=`build_dir_$arch $variant`
|
||||
rm -rf "$build_dir"
|
||||
get_asset "$libs_asset" # Thus plunks stuff into the build dir, so have to restore it now.
|
||||
# This is the way it will work in future
|
||||
#for target_dir in $target_dirs
|
||||
#do
|
||||
# mkdir -p "$build_dir/$target_dir"
|
||||
# cp "../../$fmod/api/$fmod_lib/$fmod_so" "$build_dir/$target_dir"
|
||||
#done
|
||||
#mkdir -p "$build_dir/libraries/include"
|
||||
#cp "../../$fmod/api/inc/"* "$build_dir/libraries/include"
|
||||
echo "==== $variant ====" >> build.log
|
||||
if ./develop.py \
|
||||
--unattended \
|
||||
--incredibuild \
|
||||
-t $variant \
|
||||
-G "$cmake_generator" \
|
||||
configure \
|
||||
-DVIEWER_CHANNEL:STRING="$VIEWER_CHANNEL"\
|
||||
-DVIEWER_LOGIN_CHANNEL:STRING="$VIEWER_CHANNEL"\
|
||||
-DPACKAGE:BOOL=ON >>build.log 2>&1
|
||||
then
|
||||
if ./develop.py\
|
||||
--unattended\
|
||||
--incredibuild \
|
||||
-t $variant\
|
||||
-G "$cmake_generator" \
|
||||
build prepare >>build.log 2>&1
|
||||
then
|
||||
if ./develop.py\
|
||||
--unattended\
|
||||
--incredibuild \
|
||||
-t $variant\
|
||||
-G "$cmake_generator" \
|
||||
build package >>build.log 2>&1
|
||||
then
|
||||
# run tests if needed
|
||||
true
|
||||
else
|
||||
succeeded=false
|
||||
fi
|
||||
else
|
||||
succeeded=false
|
||||
fi
|
||||
else
|
||||
succeeded=false
|
||||
fi
|
||||
done
|
||||
|
||||
# check statuis and upload results to S3
|
||||
subject=
|
||||
if $succeeded
|
||||
then
|
||||
package=`installer_$arch`
|
||||
test -r "$package" || fail not found: $package
|
||||
package_file=`echo $package | sed 's:.*/::'`
|
||||
if s3_available
|
||||
then
|
||||
echo "$PUBLIC_URL/$branch/$revision/$package_file" > "$arch"
|
||||
echo "$PUBLIC_URL/$branch/$revision/good-build.$arch" >> "$arch"
|
||||
"$helpers/s3put.sh" "$package" "$S3PUT_URL/$branch/$revision/$package_file" binary/octet-stream\
|
||||
|| fail Uploading "$package"
|
||||
"$helpers/s3put.sh" build.log "$S3PUT_URL/$branch/$revision/good-build.$arch" text/plain\
|
||||
|| fail Uploading build.log
|
||||
"$helpers/s3put.sh" "$arch" "$S3PUT_URL/$branch/$revision/$arch" text/plain\
|
||||
|| fail Uploading token file
|
||||
for symbolfile in $symbolfiles
|
||||
do
|
||||
targetfile="`echo $symbolfile | sed 's:.*/::'`"
|
||||
"$helpers/s3put.sh" "$build_dir/$symbolfile" "$S3SYMBOL_URL/$revision/$targetfile" binary/octet-stream\
|
||||
|| fail Uploading "$symbolfile"
|
||||
done
|
||||
if python "$all_done"\
|
||||
curl\
|
||||
"$S3GET_URL/$branch/$revision/$arch"\
|
||||
$other_archs > message
|
||||
then
|
||||
subject="Successful Build for $branch ($revision)"
|
||||
fi
|
||||
else
|
||||
true s3 is not available
|
||||
fi
|
||||
else
|
||||
if s3_available
|
||||
then
|
||||
"$helpers/s3put.sh" build.log "$S3PUT_URL/$branch/$revision/failed-build.$arch" text/plain\
|
||||
|| fail Uploading build.log
|
||||
subject="Failed Build for $branch ($revision) on $arch"
|
||||
cat >message <<EOF
|
||||
Build for $branch ($revision) failed for $arch.
|
||||
Please see the build log for details:
|
||||
|
||||
$PUBLIC_URL/$branch/$revision/failed-build.$arch
|
||||
|
||||
EOF
|
||||
else
|
||||
true s3 is not available
|
||||
fi
|
||||
fi
|
||||
|
||||
# We have something to say...
|
||||
if [ x"$subject" != x ]
|
||||
then
|
||||
# Extract change list since last build
|
||||
if [ x"$PARABUILD_CHANGE_LIST_NUMBER" = x ]
|
||||
then
|
||||
echo "No change information available" >> message
|
||||
elif [ x"$PARABUILD_PREVIOUS_CHANGE_LIST_NUMBER" = x ]
|
||||
then
|
||||
( cd .. && svn log --verbose --stop-on-copy --limit 50 ) >>message
|
||||
else
|
||||
if [ "$PARABUILD_PREVIOUS_CHANGE_LIST_NUMBER" -lt "$PARABUILD_CHANGE_LIST_NUMBER" ]
|
||||
then
|
||||
range=`expr 1 + "$PARABUILD_PREVIOUS_CHANGE_LIST_NUMBER"`:"$PARABUILD_CHANGE_LIST_NUMBER"
|
||||
else
|
||||
range="$PARABUILD_CHANGE_LIST_NUMBER"
|
||||
fi
|
||||
( cd .. && svn log --verbose -r"$range" ) >>message
|
||||
fi
|
||||
# $PUBLIC_EMAIL can be a list, so no quotes
|
||||
python "$mail" "$subject" $PUBLIC_EMAIL <message
|
||||
fi
|
||||
|
||||
if $succeeded
|
||||
then
|
||||
pass
|
||||
else
|
||||
fail
|
||||
fi
|
||||
|
||||
54
scripts/build_version.py
Executable file
54
scripts/build_version.py
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Print the build information embedded in a header file.
|
||||
#
|
||||
# Expects to be invoked from the command line with a file name and a
|
||||
# list of directories to search. The file name will be one of the
|
||||
# following:
|
||||
#
|
||||
# llversionserver.h
|
||||
# llversionviewer.h
|
||||
#
|
||||
# The directory list that follows will include indra/llcommon, where
|
||||
# these files live.
|
||||
|
||||
import errno, os, re
|
||||
|
||||
def get_version(filename):
|
||||
fp = open(filename)
|
||||
data = fp.read()
|
||||
fp.close()
|
||||
|
||||
vals = {}
|
||||
m = re.search('const S32 LL_VERSION_MAJOR = (\d+);', data)
|
||||
vals['major'] = m.group(1)
|
||||
m = re.search('const S32 LL_VERSION_MINOR = (\d+);', data)
|
||||
vals['minor'] = m.group(1)
|
||||
m = re.search('const S32 LL_VERSION_PATCH = (\d+);', data)
|
||||
vals['patch'] = m.group(1)
|
||||
m = re.search('const S32 LL_VERSION_BUILD = (\d+);', data)
|
||||
vals['build'] = m.group(1)
|
||||
|
||||
return "%(major)s.%(minor)s.%(patch)s.%(build)s" % vals
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
try:
|
||||
for path in sys.argv[2:]:
|
||||
name = os.path.join(path, sys.argv[1])
|
||||
try:
|
||||
print get_version(name)
|
||||
break
|
||||
except OSError, err:
|
||||
if err.errno != errno.ENOENT:
|
||||
raise
|
||||
else:
|
||||
print >> sys.stderr, 'File not found:', sys.argv[1]
|
||||
sys.exit(1)
|
||||
except AttributeError:
|
||||
print >> sys.stderr, 'Error: malformatted file: ', name
|
||||
sys.exit(1)
|
||||
except IndexError:
|
||||
print >> sys.stderr, ('Usage: %s llversion[...].h [directories]' %
|
||||
sys.argv[0])
|
||||
1151
scripts/install.py
Executable file
1151
scripts/install.py
Executable file
File diff suppressed because it is too large
Load Diff
8936
scripts/messages/message_template.msg
Normal file
8936
scripts/messages/message_template.msg
Normal file
File diff suppressed because it is too large
Load Diff
281
scripts/public_fetch_tarballs.py
Executable file
281
scripts/public_fetch_tarballs.py
Executable file
@@ -0,0 +1,281 @@
|
||||
#!/usr/bin/python
|
||||
"""\
|
||||
@file public_fetch_tarballs.py
|
||||
@author Rob Lanphier
|
||||
@date 2009-05-30
|
||||
@brief Fetch + extract tarballs and zipfiles listed in doc/asset_urls.txt
|
||||
|
||||
$LicenseInfo:firstyear=2009&license=viewergpl$
|
||||
Copyright (c) 2009, Linden Research, Inc.
|
||||
|
||||
Second Life Viewer Source Code
|
||||
The source code in this file ("Source Code") is provided by Linden Lab
|
||||
to you under the terms of the GNU General Public License, version 2.0
|
||||
("GPL"), unless you have obtained a separate licensing agreement
|
||||
("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
|
||||
There are special exceptions to the terms and conditions of the GPL as
|
||||
it is applied to this Source Code. View the full text of the exception
|
||||
in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
online at
|
||||
http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
|
||||
By copying, modifying or distributing this software, you acknowledge
|
||||
that you have read and understood your obligations described above,
|
||||
and agree to abide by those obligations.
|
||||
|
||||
ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
COMPLETENESS OR PERFORMANCE.
|
||||
$/LicenseInfo$
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
# Look for indra/lib/python in all possible parent directories ...
|
||||
# This is an improvement over the setup-path.py method used previously:
|
||||
# * the script may blocated anywhere inside the source tree
|
||||
# * it doesn't depend on the current directory
|
||||
# * it doesn't depend on another file being present.
|
||||
|
||||
def add_indra_lib_path():
|
||||
root = os.path.realpath(__file__)
|
||||
# always insert the directory of the script in the search path
|
||||
dir = os.path.dirname(root)
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
|
||||
# Now go look for indra/lib/python in the parent dies
|
||||
while root != os.path.sep:
|
||||
root = os.path.dirname(root)
|
||||
dir = os.path.join(root, 'indra', 'lib', 'python')
|
||||
if os.path.isdir(dir):
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
return root
|
||||
else:
|
||||
print >>sys.stderr, "This script is not inside a valid installation."
|
||||
sys.exit(1)
|
||||
|
||||
base_dir = add_indra_lib_path()
|
||||
print base_dir
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import urllib
|
||||
import zipfile
|
||||
import tarfile
|
||||
import optparse
|
||||
import tempfile
|
||||
|
||||
import indra.util.helpformatter
|
||||
|
||||
# load + parse doc/asset_urls.txt
|
||||
def get_asset_urls():
|
||||
asset_urls={}
|
||||
f = open(os.path.join(base_dir,"doc", "asset_urls.txt"))
|
||||
for line in f:
|
||||
line=line.strip()
|
||||
(name, value)=re.split("=", line, 1)
|
||||
asset_urls[name]=value
|
||||
return asset_urls
|
||||
|
||||
# Filename from a URL
|
||||
def get_asset_filename_from_url(asseturl, targetdir):
|
||||
i = asseturl.rfind('/')
|
||||
filename = os.path.join(targetdir, asseturl[i+1:])
|
||||
return filename
|
||||
|
||||
|
||||
# Extract .zip file to targetdir. Called by extract_archive_sans_linden.
|
||||
def extract_zipfile_sans_linden(filename, targetdir):
|
||||
archive = zipfile.ZipFile(filename, 'r')
|
||||
names = archive.namelist()
|
||||
for path in names:
|
||||
if(path=="linden/"):
|
||||
pass
|
||||
target = os.path.join(targetdir, re.sub("linden/", "", path))
|
||||
subdir = os.path.dirname(target)
|
||||
if not os.path.exists(subdir):
|
||||
os.makedirs(subdir)
|
||||
if not os.path.exists(target):
|
||||
fp = open(target, 'wb')
|
||||
fp.write(archive.read(path))
|
||||
fp.close()
|
||||
archive.close()
|
||||
|
||||
|
||||
# Extract .tar.gz file to targetdir. Called by extract_archive_sans_linden.
|
||||
def extract_tarball_sans_linden(filename, targetdir):
|
||||
archive = tarfile.TarFile.open(filename, 'r')
|
||||
# get a series of TarInfo objects
|
||||
tarentries=archive.getmembers()
|
||||
for tarentry in tarentries:
|
||||
target = re.sub(r'^(\./)?(linden/)?', "", tarentry.name)
|
||||
if(target==""):
|
||||
continue
|
||||
fulltarget=os.path.join(targetdir, target)
|
||||
subdir = os.path.dirname(fulltarget)
|
||||
if not os.path.exists(subdir):
|
||||
os.makedirs(subdir)
|
||||
if not os.path.exists(fulltarget):
|
||||
# Reset the name property on the TarInfo object, so it writes the
|
||||
# file exactly where we want it. It's hard telling for sure if this
|
||||
# property is intended to be written to, but it works for now.
|
||||
tarentry.name=fulltarget
|
||||
# Calling TarFile.extract with the "path" parameter doesn't work as
|
||||
# we might hope, because the path components in the tarball get
|
||||
# appended to the "path" parameter. Hence the reason for passing in
|
||||
# the TarInfo object with the munged name property
|
||||
archive.extract(tarentry)
|
||||
archive.close()
|
||||
|
||||
# Extract either .tar.gz file or .zip file to targetdir, stripping off the
|
||||
# leading "linden" directory, but leaving the directory structure otherwise
|
||||
# intact.
|
||||
def extract_archive_sans_linden(filename, targetdir):
|
||||
if(filename.endswith('.tar.gz')):
|
||||
extract_tarball_sans_linden(filename, targetdir)
|
||||
elif(filename.endswith('.zip')):
|
||||
extract_zipfile_sans_linden(filename, targetdir)
|
||||
else:
|
||||
raise Exception, "Unhandled archive type"
|
||||
|
||||
def get_assetnames_by_platform(platform):
|
||||
assetnames=['SLASSET_ART']
|
||||
if(platform=='linux' or platform=='all'):
|
||||
assetnames.append('SLASSET_LIBS_LINUXI386')
|
||||
if(platform=='darwin' or platform=='all'):
|
||||
assetnames.append('SLASSET_LIBS_DARWIN')
|
||||
if(platform=='windows' or platform=='all'):
|
||||
assetnames.append('SLASSET_LIBS_WIN32')
|
||||
return assetnames
|
||||
|
||||
# adapted from install.py
|
||||
def _get_platform():
|
||||
"Return appropriate platform packages for the environment."
|
||||
platform_map = {
|
||||
'darwin': 'darwin',
|
||||
'linux2': 'linux',
|
||||
'win32' : 'windows',
|
||||
'cygwin' : 'windows',
|
||||
'solaris' : 'solaris'
|
||||
}
|
||||
this_platform = platform_map[sys.platform]
|
||||
return this_platform
|
||||
|
||||
# copied from install.py
|
||||
def _default_installable_cache():
|
||||
"""In general, the installable files do not change much, so find a
|
||||
host/user specific location to cache files."""
|
||||
user = _getuser()
|
||||
cache_dir = "/var/tmp/%s/install.cache" % user
|
||||
if _get_platform() == 'windows':
|
||||
cache_dir = os.path.join(tempfile.gettempdir(), \
|
||||
'install.cache.%s' % user)
|
||||
return cache_dir
|
||||
|
||||
# For status messages (e.g. "Loading..."). May come in handy if
|
||||
# we implement a "quiet" mode.
|
||||
def _report(string):
|
||||
print string
|
||||
|
||||
|
||||
# copied from install.py
|
||||
def _getuser():
|
||||
"Get the user"
|
||||
try:
|
||||
# Unix-only.
|
||||
import getpass
|
||||
return getpass.getuser()
|
||||
except ImportError:
|
||||
import win32api
|
||||
return win32api.GetUserName()
|
||||
|
||||
# adapted from install.py
|
||||
def _parse_args():
|
||||
parser = optparse.OptionParser(
|
||||
usage="usage: %prog [options]",
|
||||
formatter = indra.util.helpformatter.Formatter(),
|
||||
description="""This script fetches and installs tarballs and \
|
||||
zipfiles ("asset bundles") listed in doc/asset_urls.txt
|
||||
|
||||
If no asset bundles are specified on the command line, then the default \
|
||||
behavior is to install all known asset bundles appropriate for the platform \
|
||||
specified. You can specify more than one asset bundle on the command line.
|
||||
|
||||
Example:
|
||||
%prog SLASSET_ART
|
||||
|
||||
This looks for the "SLASSET_ART" entry in doc/asset_urls.txt, and extracts
|
||||
the corresponding asset bundle into your source tree.
|
||||
""")
|
||||
parser.add_option(
|
||||
'-p', '--platform',
|
||||
type='choice',
|
||||
default=_get_platform(),
|
||||
dest='platform',
|
||||
choices=['windows', 'darwin', 'linux', 'solaris', 'all'],
|
||||
help="""Override the automatically determined platform. \
|
||||
You can specify 'all' to get assets for all platforms. Choices: windows, \
|
||||
darwin, linux, solaris, or all. Default: autodetected (%s)""" % \
|
||||
_get_platform())
|
||||
parser.add_option(
|
||||
'--cache-dir',
|
||||
type='string',
|
||||
default=_default_installable_cache(),
|
||||
dest='cache_dir',
|
||||
help='Where to download files. Default: %s'% \
|
||||
(_default_installable_cache()))
|
||||
parser.add_option(
|
||||
'--install-dir',
|
||||
type='string',
|
||||
default=base_dir,
|
||||
dest='install_dir',
|
||||
help='Where to unpack the installed files. Default: %s' % base_dir)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def main(argv):
|
||||
options, args = _parse_args()
|
||||
# 1. prepare cache dir
|
||||
if not os.path.exists(options.cache_dir):
|
||||
os.makedirs(options.cache_dir)
|
||||
|
||||
# 2. read doc/asset_urls.txt
|
||||
asseturls=get_asset_urls()
|
||||
|
||||
# 3. figure out which asset bundles we'll be downloading
|
||||
if len(args)>0:
|
||||
assetnames=args
|
||||
else:
|
||||
assetnames=get_assetnames_by_platform(options.platform)
|
||||
|
||||
# 4. download and extract each asset bundle
|
||||
for asset in assetnames:
|
||||
# 4a. get the URL for the asset bundle
|
||||
try:
|
||||
asseturl=asseturls[asset]
|
||||
except:
|
||||
print "No asset in doc/asset_urls.txt named %s" % asset
|
||||
sys.exit(2)
|
||||
# 4b. figure out where to put the downloaded asset bundle
|
||||
filename=get_asset_filename_from_url(asseturl, options.cache_dir)
|
||||
|
||||
# 4c. see if we have it, and if not, get it
|
||||
if os.path.exists(filename):
|
||||
_report("Using already downloaded "+filename+" ...")
|
||||
else:
|
||||
_report("Downloading "+filename+" ...")
|
||||
urllib.urlretrieve(asseturl, filename)
|
||||
# 4d. extract it into the tree
|
||||
extract_archive_sans_linden(filename, options.install_dir)
|
||||
|
||||
# execute main() only if invoked directly:
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
|
||||
44
scripts/setup-path.py
Executable file
44
scripts/setup-path.py
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/python
|
||||
"""\
|
||||
@file setup-path.py
|
||||
@brief Get the python library directory in the path, so we don't have
|
||||
to screw with PYTHONPATH or symbolic links.
|
||||
|
||||
$LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
|
||||
Copyright (c) 2007-2009, Linden Research, Inc.
|
||||
|
||||
Second Life Viewer Source Code
|
||||
The source code in this file ("Source Code") is provided by Linden Lab
|
||||
to you under the terms of the GNU General Public License, version 2.0
|
||||
("GPL"), unless you have obtained a separate licensing agreement
|
||||
("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
|
||||
There are special exceptions to the terms and conditions of the GPL as
|
||||
it is applied to this Source Code. View the full text of the exception
|
||||
in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
online at
|
||||
http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
|
||||
By copying, modifying or distributing this software, you acknowledge
|
||||
that you have read and understood your obligations described above,
|
||||
and agree to abide by those obligations.
|
||||
|
||||
ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
COMPLETENESS OR PERFORMANCE.
|
||||
$/LicenseInfo$
|
||||
"""
|
||||
|
||||
import sys
|
||||
from os.path import realpath, dirname, join
|
||||
|
||||
# Walk back to checkout base directory
|
||||
dir = dirname(dirname(realpath(__file__)))
|
||||
# Walk in to libraries directory
|
||||
dir = join(dir, 'indra', 'lib', 'python')
|
||||
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
312
scripts/template_verifier.py
Executable file
312
scripts/template_verifier.py
Executable file
@@ -0,0 +1,312 @@
|
||||
#!/usr/bin/python
|
||||
"""\
|
||||
@file template_verifier.py
|
||||
@brief Message template compatibility verifier.
|
||||
|
||||
$LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
|
||||
Copyright (c) 2007-2009, Linden Research, Inc.
|
||||
|
||||
Second Life Viewer Source Code
|
||||
The source code in this file ("Source Code") is provided by Linden Lab
|
||||
to you under the terms of the GNU General Public License, version 2.0
|
||||
("GPL"), unless you have obtained a separate licensing agreement
|
||||
("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
|
||||
There are special exceptions to the terms and conditions of the GPL as
|
||||
it is applied to this Source Code. View the full text of the exception
|
||||
in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
online at
|
||||
http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
|
||||
By copying, modifying or distributing this software, you acknowledge
|
||||
that you have read and understood your obligations described above,
|
||||
and agree to abide by those obligations.
|
||||
|
||||
ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
COMPLETENESS OR PERFORMANCE.
|
||||
$/LicenseInfo$
|
||||
"""
|
||||
|
||||
"""template_verifier is a script which will compare the
|
||||
current repository message template with the "master" message template, accessible
|
||||
via http://secondlife.com/app/message_template/master_message_template.msg
|
||||
If [FILE] is specified, it will be checked against the master template.
|
||||
If [FILE] [FILE] is specified, two local files will be checked against
|
||||
each other.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
# Look for indra/lib/python in all possible parent directories ...
|
||||
# This is an improvement over the setup-path.py method used previously:
|
||||
# * the script may blocated anywhere inside the source tree
|
||||
# * it doesn't depend on the current directory
|
||||
# * it doesn't depend on another file being present.
|
||||
|
||||
def add_indra_lib_path():
|
||||
root = os.path.realpath(__file__)
|
||||
# always insert the directory of the script in the search path
|
||||
dir = os.path.dirname(root)
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
|
||||
# Now go look for indra/lib/python in the parent dies
|
||||
while root != os.path.sep:
|
||||
root = os.path.dirname(root)
|
||||
dir = os.path.join(root, 'indra', 'lib', 'python')
|
||||
if os.path.isdir(dir):
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
break
|
||||
else:
|
||||
print >>sys.stderr, "This script is not inside a valid installation."
|
||||
sys.exit(1)
|
||||
|
||||
add_indra_lib_path()
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import urllib
|
||||
|
||||
from indra.ipc import compatibility
|
||||
from indra.ipc import tokenstream
|
||||
from indra.ipc import llmessage
|
||||
|
||||
def getstatusall(command):
|
||||
""" Like commands.getstatusoutput, but returns stdout and
|
||||
stderr separately(to get around "killed by signal 15" getting
|
||||
included as part of the file). Also, works on Windows."""
|
||||
(input, out, err) = os.popen3(command, 't')
|
||||
status = input.close() # send no input to the command
|
||||
output = out.read()
|
||||
error = err.read()
|
||||
status = out.close()
|
||||
status = err.close() # the status comes from the *last* pipe that is closed
|
||||
return status, output, error
|
||||
|
||||
def getstatusoutput(command):
|
||||
status, output, error = getstatusall(command)
|
||||
return status, output
|
||||
|
||||
|
||||
def die(msg):
|
||||
print >>sys.stderr, msg
|
||||
sys.exit(1)
|
||||
|
||||
MESSAGE_TEMPLATE = 'message_template.msg'
|
||||
|
||||
PRODUCTION_ACCEPTABLE = (compatibility.Same, compatibility.Newer)
|
||||
DEVELOPMENT_ACCEPTABLE = (
|
||||
compatibility.Same, compatibility.Newer,
|
||||
compatibility.Older, compatibility.Mixed)
|
||||
|
||||
MAX_MASTER_AGE = 60 * 60 * 4 # refresh master cache every 4 hours
|
||||
|
||||
def retry(times, function, *args, **kwargs):
|
||||
for i in range(times):
|
||||
try:
|
||||
return function(*args, **kwargs)
|
||||
except Exception, e:
|
||||
if i == times - 1:
|
||||
raise e # we retried all the times we could
|
||||
|
||||
def compare(base_parsed, current_parsed, mode):
|
||||
"""Compare the current template against the base template using the given
|
||||
'mode' strictness:
|
||||
|
||||
development: Allows Same, Newer, Older, and Mixed
|
||||
production: Allows only Same or Newer
|
||||
|
||||
Print out information about whether the current template is compatible
|
||||
with the base template.
|
||||
|
||||
Returns a tuple of (bool, Compatibility)
|
||||
Return True if they are compatible in this mode, False if not.
|
||||
"""
|
||||
|
||||
compat = current_parsed.compatibleWithBase(base_parsed)
|
||||
if mode == 'production':
|
||||
acceptable = PRODUCTION_ACCEPTABLE
|
||||
else:
|
||||
acceptable = DEVELOPMENT_ACCEPTABLE
|
||||
|
||||
if type(compat) in acceptable:
|
||||
return True, compat
|
||||
return False, compat
|
||||
|
||||
def fetch(url):
|
||||
if url.startswith('file://'):
|
||||
# just open the file directly because urllib is dumb about these things
|
||||
file_name = url[len('file://'):]
|
||||
return open(file_name).read()
|
||||
else:
|
||||
# *FIX: this doesn't throw an exception for a 404, and oddly enough the sl.com 404 page actually gets parsed successfully
|
||||
return ''.join(urllib.urlopen(url).readlines())
|
||||
|
||||
def cache_master(master_url):
|
||||
"""Using the url for the master, updates the local cache, and returns an url to the local cache."""
|
||||
master_cache = local_master_cache_filename()
|
||||
master_cache_url = 'file://' + master_cache
|
||||
# decide whether to refresh the master cache based on its age
|
||||
import time
|
||||
if (os.path.exists(master_cache)
|
||||
and time.time() - os.path.getmtime(master_cache) < MAX_MASTER_AGE):
|
||||
return master_cache_url # our cache is fresh
|
||||
# new master doesn't exist or isn't fresh
|
||||
print "Refreshing master cache from %s" % master_url
|
||||
def get_and_test_master():
|
||||
new_master_contents = fetch(master_url)
|
||||
llmessage.parseTemplateString(new_master_contents)
|
||||
return new_master_contents
|
||||
try:
|
||||
new_master_contents = retry(3, get_and_test_master)
|
||||
except IOError, e:
|
||||
# the refresh failed, so we should just soldier on
|
||||
print "WARNING: unable to download new master, probably due to network error. Your message template compatibility may be suspect."
|
||||
print "Cause: %s" % e
|
||||
return master_cache_url
|
||||
try:
|
||||
tmpname = '%s.%d' % (master_cache, os.getpid())
|
||||
mc = open(tmpname, 'wb')
|
||||
mc.write(new_master_contents)
|
||||
mc.close()
|
||||
try:
|
||||
os.rename(tmpname, master_cache)
|
||||
except OSError:
|
||||
# We can't rename atomically on top of an existing file on
|
||||
# Windows. Unlinking the existing file will fail if the
|
||||
# file is being held open by a process, but there's only
|
||||
# so much working around a lame I/O API one can take in
|
||||
# a single day.
|
||||
os.unlink(master_cache)
|
||||
os.rename(tmpname, master_cache)
|
||||
except IOError, e:
|
||||
print "WARNING: Unable to write master message template to %s, proceeding without cache." % master_cache
|
||||
print "Cause: %s" % e
|
||||
return master_url
|
||||
return master_cache_url
|
||||
|
||||
def local_template_filename():
|
||||
"""Returns the message template's default location relative to template_verifier.py:
|
||||
./messages/message_template.msg."""
|
||||
d = os.path.dirname(os.path.realpath(__file__))
|
||||
return os.path.join(d, 'messages', MESSAGE_TEMPLATE)
|
||||
|
||||
def getuser():
|
||||
try:
|
||||
# Unix-only.
|
||||
import getpass
|
||||
return getpass.getuser()
|
||||
except ImportError:
|
||||
import win32api
|
||||
return win32api.GetUserName()
|
||||
|
||||
def local_master_cache_filename():
|
||||
"""Returns the location of the master template cache (which is in the system tempdir)
|
||||
<temp_dir>/master_message_template_cache.msg"""
|
||||
import tempfile
|
||||
d = tempfile.gettempdir()
|
||||
user = getuser()
|
||||
return os.path.join(d, 'master_message_template_cache.%s.msg' % user)
|
||||
|
||||
|
||||
def run(sysargs):
|
||||
parser = optparse.OptionParser(
|
||||
usage="usage: %prog [FILE] [FILE]",
|
||||
description=__doc__)
|
||||
parser.add_option(
|
||||
'-m', '--mode', type='string', dest='mode',
|
||||
default='development',
|
||||
help="""[development|production] The strictness mode to use
|
||||
while checking the template; see the wiki page for details about
|
||||
what is allowed and disallowed by each mode:
|
||||
http://wiki.secondlife.com/wiki/Template_verifier.py
|
||||
""")
|
||||
parser.add_option(
|
||||
'-u', '--master_url', type='string', dest='master_url',
|
||||
default='http://secondlife.com/app/message_template/master_message_template.msg',
|
||||
help="""The url of the master message template.""")
|
||||
parser.add_option(
|
||||
'-c', '--cache_master', action='store_true', dest='cache_master',
|
||||
default=False, help="""Set to true to attempt use local cached copy of the master template.""")
|
||||
|
||||
options, args = parser.parse_args(sysargs)
|
||||
|
||||
if options.mode == 'production':
|
||||
options.cache_master = False
|
||||
|
||||
# both current and master supplied in positional params
|
||||
if len(args) == 2:
|
||||
master_filename, current_filename = args
|
||||
print "master:", master_filename
|
||||
print "current:", current_filename
|
||||
master_url = 'file://%s' % master_filename
|
||||
current_url = 'file://%s' % current_filename
|
||||
# only current supplied in positional param
|
||||
elif len(args) == 1:
|
||||
master_url = None
|
||||
current_filename = args[0]
|
||||
print "master:", options.master_url
|
||||
print "current:", current_filename
|
||||
current_url = 'file://%s' % current_filename
|
||||
# nothing specified, use defaults for everything
|
||||
elif len(args) == 0:
|
||||
master_url = None
|
||||
current_url = None
|
||||
else:
|
||||
die("Too many arguments")
|
||||
|
||||
if master_url is None:
|
||||
master_url = options.master_url
|
||||
|
||||
if current_url is None:
|
||||
current_filename = local_template_filename()
|
||||
print "master:", options.master_url
|
||||
print "current:", current_filename
|
||||
current_url = 'file://%s' % current_filename
|
||||
|
||||
# retrieve the contents of the local template and check for syntax
|
||||
current = fetch(current_url)
|
||||
current_parsed = llmessage.parseTemplateString(current)
|
||||
|
||||
if options.cache_master:
|
||||
# optionally return a url to a locally-cached master so we don't hit the network all the time
|
||||
master_url = cache_master(master_url)
|
||||
|
||||
def parse_master_url():
|
||||
master = fetch(master_url)
|
||||
return llmessage.parseTemplateString(master)
|
||||
try:
|
||||
master_parsed = retry(3, parse_master_url)
|
||||
except (IOError, tokenstream.ParseError), e:
|
||||
if options.mode == 'production':
|
||||
raise e
|
||||
else:
|
||||
print "WARNING: problems retrieving the master from %s." % master_url
|
||||
print "Syntax-checking the local template ONLY, no compatibility check is being run."
|
||||
print "Cause: %s\n\n" % e
|
||||
return 0
|
||||
|
||||
acceptable, compat = compare(
|
||||
master_parsed, current_parsed, options.mode)
|
||||
|
||||
def explain(header, compat):
|
||||
print header
|
||||
# indent compatibility explanation
|
||||
print '\n\t'.join(compat.explain().split('\n'))
|
||||
|
||||
if acceptable:
|
||||
explain("--- PASS ---", compat)
|
||||
else:
|
||||
explain("*** FAIL ***", compat)
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(run(sys.argv[1:]))
|
||||
|
||||
|
||||
203
scripts/update_source_contributors.py
Executable file
203
scripts/update_source_contributors.py
Executable file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: UTF-8 -*-
|
||||
"""\
|
||||
@file update_source_contributors.py
|
||||
@brief Update the source contributor list in one place, instead of having
|
||||
to substitute by hand. Derived from update_viewer_version.py
|
||||
|
||||
$LicenseInfo:firstyear=2006&license=viewergpl$
|
||||
|
||||
Copyright (c) 2006-2009, Linden Research, Inc.
|
||||
|
||||
Second Life Viewer Source Code
|
||||
The source code in this file ("Source Code") is provided by Linden Lab
|
||||
to you under the terms of the GNU General Public License, version 2.0
|
||||
("GPL"), unless you have obtained a separate licensing agreement
|
||||
("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
|
||||
There are special exceptions to the terms and conditions of the GPL as
|
||||
it is applied to this Source Code. View the full text of the exception
|
||||
in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
online at
|
||||
http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
|
||||
By copying, modifying or distributing this software, you acknowledge
|
||||
that you have read and understood your obligations described above,
|
||||
and agree to abide by those obligations.
|
||||
|
||||
ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
COMPLETENESS OR PERFORMANCE.
|
||||
$/LicenseInfo$
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
import string
|
||||
import getopt, os, re, commands
|
||||
|
||||
|
||||
lang_sep={"es":", ",
|
||||
"en-us":", ",
|
||||
"ru":", ",
|
||||
"hu":", ",
|
||||
"pl":", ",
|
||||
"ko":", ",
|
||||
"tr":", ",
|
||||
"it":", ",
|
||||
"da":", ",
|
||||
"pt":", ",
|
||||
"de":", ",
|
||||
"zh":", ",
|
||||
"fr":", ",
|
||||
"ja":"、",
|
||||
"uk":", "}
|
||||
|
||||
intro="Snowglobe includes source code contributions of the following residents: "
|
||||
|
||||
def add_indra_lib_path():
|
||||
root = os.path.realpath(__file__)
|
||||
# always insert the directory of the script in the search path
|
||||
dir = os.path.dirname(root)
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
|
||||
# Now go look for indra/lib/python in the parent dies
|
||||
while root != os.path.sep:
|
||||
root = os.path.dirname(root)
|
||||
dir = os.path.join(root, 'indra', 'lib', 'python')
|
||||
if os.path.isdir(dir):
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
return root
|
||||
else:
|
||||
print >>sys.stderr, "This script is not inside a valid installation."
|
||||
sys.exit(1)
|
||||
|
||||
src_root = add_indra_lib_path()
|
||||
|
||||
from indra.util import llversion
|
||||
|
||||
|
||||
def usage():
|
||||
print "Usage:"
|
||||
print sys.argv[0] + """ [options]
|
||||
|
||||
Options:
|
||||
--version
|
||||
Specify the version string to replace current version.
|
||||
--skip-on-branch
|
||||
Specify a regular expression against which the current branch
|
||||
is matched. If it matches, then leave version strings alone.
|
||||
Use this to avoid changing version strings on release candidate
|
||||
builds.
|
||||
--src-root
|
||||
Location of source tree
|
||||
--verbose
|
||||
--help
|
||||
Print this message and exit.
|
||||
|
||||
"""
|
||||
def _getstatusoutput(cmd):
|
||||
"""Return Win32 (status, output) of executing cmd
|
||||
in a shell."""
|
||||
if os.path.sep != "/":
|
||||
# stupid #%#$$ windows
|
||||
cmd = 'cmd.exe /c "'+cmd+'"'
|
||||
pipe = os.popen(cmd, 'r')
|
||||
text = pipe.read()
|
||||
sts = pipe.close()
|
||||
if sts is None: sts = 0
|
||||
if text[-1:] == '\n': text = text[:-1]
|
||||
return sts, text
|
||||
|
||||
def get_contributors(filename):
|
||||
f = open(filename)
|
||||
|
||||
credits={}
|
||||
contributors=[]
|
||||
thisres=''
|
||||
for line in f:
|
||||
m = re.search('^(\S+ +\S+)\s*$', line.strip())
|
||||
if(m):
|
||||
thisres=m.group(1)
|
||||
credits[thisres]=[]
|
||||
contributors.append(thisres)
|
||||
m = re.search('^\s+(\S+\-\d+)\s*$', line)
|
||||
if(m and thisres != ''):
|
||||
credits[thisres].append(m.group(1))
|
||||
return contributors
|
||||
|
||||
def main():
|
||||
global src_root
|
||||
global intro
|
||||
svn = os.path.expandvars("${SVN}")
|
||||
if not svn or svn == "${SVN}": svn = "svn"
|
||||
|
||||
contributors = get_contributors(src_root + "/doc/contributions.txt")
|
||||
|
||||
re_map = {}
|
||||
|
||||
#re_map['filename'] = (('pattern', 'replacement'),
|
||||
# ('pattern', 'replacement')
|
||||
|
||||
for lang in lang_sep.keys():
|
||||
filename='indra/newview/skins/default/xui/%(lang)s/floater_about.xml' % locals()
|
||||
searchpattern='%(intro)s.*\n' % {'intro':intro}
|
||||
replacelist=string.join(contributors, lang_sep[lang])
|
||||
replacement='%(intro)s%(replacelist)s\n' % {'intro':intro,'replacelist':replacelist}
|
||||
re_map[filename] = [[searchpattern, replacement]]
|
||||
|
||||
verbose = False
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:],
|
||||
"",
|
||||
['skip-on-branch=',
|
||||
'src-root=',
|
||||
'verbose',
|
||||
'help'])
|
||||
skip_on_branch_re = None
|
||||
for o,a in opts:
|
||||
if o in ('--skip-on-branch'):
|
||||
skip_on_branch_re = re.compile(a)
|
||||
if o in ('--src-root'):
|
||||
src_root = a
|
||||
if o in ('--verbose'):
|
||||
verbose = True
|
||||
if o in ('--help'):
|
||||
usage()
|
||||
return 0
|
||||
|
||||
if verbose:
|
||||
print "Source Path:", src_root
|
||||
if viewer_channel != None:
|
||||
print "Current viewer channel/version: '%(viewer_channel)s' / '%(viewer_version)s'" % locals()
|
||||
print
|
||||
|
||||
# Iterate through all of the files in the map, and apply the
|
||||
# substitution filters
|
||||
for filename in re_map.keys():
|
||||
try:
|
||||
# Read the entire file into a string
|
||||
full_fn = src_root + '/' + filename
|
||||
file = open(full_fn,"r")
|
||||
file_str = file.read()
|
||||
file.close()
|
||||
|
||||
if verbose:
|
||||
print "Processing file:",filename
|
||||
for rule in re_map[filename]:
|
||||
repl = rule[1]
|
||||
file_str = re.sub(rule[0], repl, file_str)
|
||||
|
||||
file = open(full_fn,"w")
|
||||
file.write(file_str)
|
||||
file.close()
|
||||
except IOError:
|
||||
print "File %(filename)s not present, skipping..." % locals()
|
||||
return 0
|
||||
|
||||
main()
|
||||
|
||||
307
scripts/update_version_files.py
Executable file
307
scripts/update_version_files.py
Executable file
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/python
|
||||
"""\
|
||||
@file update_version_files.py
|
||||
@brief Update all of the various files in the repository to a new
|
||||
version number, instead of having to figure it out by hand
|
||||
|
||||
$LicenseInfo:firstyear=2006&license=viewergpl$
|
||||
|
||||
Copyright (c) 2006-2009, Linden Research, Inc.
|
||||
|
||||
Second Life Viewer Source Code
|
||||
The source code in this file ("Source Code") is provided by Linden Lab
|
||||
to you under the terms of the GNU General Public License, version 2.0
|
||||
("GPL"), unless you have obtained a separate licensing agreement
|
||||
("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
|
||||
There are special exceptions to the terms and conditions of the GPL as
|
||||
it is applied to this Source Code. View the full text of the exception
|
||||
in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
online at
|
||||
http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
|
||||
By copying, modifying or distributing this software, you acknowledge
|
||||
that you have read and understood your obligations described above,
|
||||
and agree to abide by those obligations.
|
||||
|
||||
ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
COMPLETENESS OR PERFORMANCE.
|
||||
$/LicenseInfo$
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
def add_indra_lib_path():
|
||||
root = os.path.realpath(__file__)
|
||||
# always insert the directory of the script in the search path
|
||||
dir = os.path.dirname(root)
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
|
||||
# Now go look for indra/lib/python in the parent dies
|
||||
while root != os.path.sep:
|
||||
root = os.path.dirname(root)
|
||||
dir = os.path.join(root, 'indra', 'lib', 'python')
|
||||
if os.path.isdir(dir):
|
||||
if dir not in sys.path:
|
||||
sys.path.insert(0, dir)
|
||||
return root
|
||||
else:
|
||||
print >>sys.stderr, "This script is not inside a valid installation."
|
||||
sys.exit(1)
|
||||
|
||||
add_indra_lib_path()
|
||||
|
||||
import getopt, os, re, commands
|
||||
from indra.util import llversion
|
||||
|
||||
svn = os.path.expandvars("${SVN}")
|
||||
if not svn or svn == "${SVN}": svn = "svn"
|
||||
|
||||
def usage():
|
||||
print "Usage:"
|
||||
print sys.argv[0] + """ [options]
|
||||
|
||||
Options:
|
||||
--version
|
||||
Specify the version string to replace current version.
|
||||
--skip-on-branch
|
||||
Specify a regular expression against which the current branch
|
||||
is matched. If it matches, then leave version strings alone.
|
||||
Use this to avoid changing version strings on release candidate
|
||||
builds.
|
||||
--server
|
||||
Update llversionserver.h only with new version
|
||||
--viewer
|
||||
Update llversionviewer.h only with new version
|
||||
--channel
|
||||
Specify the viewer channel string to replace current channel.
|
||||
--server_channel
|
||||
Specify the server channel string to replace current channel.
|
||||
--src-root
|
||||
Location of source tree
|
||||
--verbose
|
||||
--help
|
||||
Print this message and exit.
|
||||
|
||||
Common Uses:
|
||||
# Update server and viewer build numbers to the current SVN revision:
|
||||
update_version_files.py
|
||||
|
||||
# Update build numbers unless we are on a release branch:
|
||||
update_version_files.py --skip-on-branch='^Branch_'
|
||||
|
||||
# Update server and viewer version numbers explicitly:
|
||||
update_version_files.py --version=1.18.1.6
|
||||
|
||||
# Update just the viewer version number explicitly:
|
||||
update_version_files.py --viewer --version=1.18.1.6
|
||||
|
||||
# Update just the server build number to the current SVN revision:
|
||||
update_version_files.py --server
|
||||
|
||||
# Update the viewer channel
|
||||
update_version_files.py --channel="First Look Puppeteering"
|
||||
|
||||
# Update the server channel
|
||||
update_version_files.py --server_channel="Het Grid"
|
||||
|
||||
"""
|
||||
def _getstatusoutput(cmd):
|
||||
"""Return Win32 (status, output) of executing cmd
|
||||
in a shell."""
|
||||
if os.path.sep != "/":
|
||||
# stupid #%#$$ windows
|
||||
cmd = 'cmd.exe /c "'+cmd+'"'
|
||||
pipe = os.popen(cmd, 'r')
|
||||
text = pipe.read()
|
||||
sts = pipe.close()
|
||||
if sts is None: sts = 0
|
||||
if text[-1:] == '\n': text = text[:-1]
|
||||
return sts, text
|
||||
|
||||
re_map = {}
|
||||
|
||||
#re_map['filename'] = (('pattern', 'replacement'),
|
||||
# ('pattern', 'replacement')
|
||||
re_map['indra/llcommon/llversionviewer.h'] = \
|
||||
(('const S32 LL_VERSION_MAJOR = (\d+);',
|
||||
'const S32 LL_VERSION_MAJOR = %(VER_MAJOR)s;'),
|
||||
('const S32 LL_VERSION_MINOR = (\d+);',
|
||||
'const S32 LL_VERSION_MINOR = %(VER_MINOR)s;'),
|
||||
('const S32 LL_VERSION_PATCH = (\d+);',
|
||||
'const S32 LL_VERSION_PATCH = %(VER_PATCH)s;'),
|
||||
('const S32 LL_VERSION_BUILD = (\d+);',
|
||||
'const S32 LL_VERSION_BUILD = %(VER_BUILD)s;'),
|
||||
('const char \* const LL_CHANNEL = "(.+)";',
|
||||
'const char * const LL_CHANNEL = "%(VIEWER_CHANNEL)s";'))
|
||||
re_map['indra/newview/res/viewerRes.rc'] = \
|
||||
(('FILEVERSION [0-9,]+',
|
||||
'FILEVERSION %(VER_MAJOR)s,%(VER_MINOR)s,%(VER_PATCH)s,%(VER_BUILD)s'),
|
||||
('PRODUCTVERSION [0-9,]+',
|
||||
'PRODUCTVERSION %(VER_MAJOR)s,%(VER_MINOR)s,%(VER_PATCH)s,%(VER_BUILD)s'),
|
||||
('VALUE "FileVersion", "[0-9.]+"',
|
||||
'VALUE "FileVersion", "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s"'),
|
||||
('VALUE "ProductVersion", "[0-9.]+"',
|
||||
'VALUE "ProductVersion", "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s"'))
|
||||
|
||||
# Trailing ',' in top level tuple is special form to avoid parsing issues with one element tuple
|
||||
re_map['indra/newview/Info-SecondLife.plist'] = \
|
||||
(('<key>CFBundleVersion</key>\n\t<string>[0-9.]+</string>',
|
||||
'<key>CFBundleVersion</key>\n\t<string>%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s</string>'),)
|
||||
|
||||
re_map['indra/newview/Info-Snowglobe.plist'] = \
|
||||
(('<key>CFBundleVersion</key>\n\t<string>[0-9.]+</string>',
|
||||
'<key>CFBundleVersion</key>\n\t<string>%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s</string>'),)
|
||||
|
||||
# This will probably only work as long as InfoPlist.strings is NOT UTF16, which is should be...
|
||||
re_map['indra/newview/English.lproj/InfoPlist.strings'] = \
|
||||
(('CFBundleShortVersionString = "Second Life version [0-9.]+";',
|
||||
'CFBundleShortVersionString = "Second Life version %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s";'),
|
||||
('CFBundleGetInfoString = "Second Life version [0-9.]+',
|
||||
'CFBundleGetInfoString = "Second Life version %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s'))
|
||||
|
||||
|
||||
version_re = re.compile('(\d+).(\d+).(\d+).(\d+)')
|
||||
svn_branch_re = re.compile('^URL:\s+\S+/([^/\s]+)$', re.MULTILINE)
|
||||
svn_revision_re = re.compile('^Last Changed Rev: (\d+)$', re.MULTILINE)
|
||||
|
||||
def main():
|
||||
script_path = os.path.dirname(__file__)
|
||||
src_root = script_path + "/../"
|
||||
verbose = False
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:],
|
||||
"",
|
||||
['version=',
|
||||
'channel=',
|
||||
'server_channel=',
|
||||
'skip-on-branch=',
|
||||
'src-root=',
|
||||
'verbose',
|
||||
'viewer',
|
||||
'help'])
|
||||
update_viewer = False
|
||||
new_version = None
|
||||
new_viewer_channel = None
|
||||
skip_on_branch_re = None
|
||||
for o,a in opts:
|
||||
if o in ('--version'):
|
||||
new_version = a
|
||||
if o in ('--skip-on-branch'):
|
||||
skip_on_branch_re = re.compile(a)
|
||||
if o in ('--channel'):
|
||||
new_viewer_channel = a
|
||||
if o in ('--src-root'):
|
||||
src_root = a
|
||||
if o in ('--verbose'):
|
||||
verbose = True
|
||||
if o in ('--server'):
|
||||
update_server = True
|
||||
if o in ('--viewer'):
|
||||
update_viewer = True
|
||||
if o in ('--help'):
|
||||
usage()
|
||||
return 0
|
||||
|
||||
if not(update_viewer):
|
||||
update_viewer = True
|
||||
|
||||
# Get current channel/version from llversion*.h
|
||||
try:
|
||||
viewer_channel = llversion.get_viewer_channel()
|
||||
viewer_version = llversion.get_viewer_version()
|
||||
except IOError:
|
||||
print "Viewer version file not present, skipping..."
|
||||
viewer_channel = None
|
||||
viewer_version = None
|
||||
update_viewer = False
|
||||
|
||||
if verbose:
|
||||
print "Source Path:", src_root
|
||||
if viewer_channel != None:
|
||||
print "Current viewer channel/version: '%(viewer_channel)s' / '%(viewer_version)s'" % locals()
|
||||
print
|
||||
|
||||
# Determine new channel(s)
|
||||
if new_viewer_channel != None and len(new_viewer_channel) > 0:
|
||||
viewer_channel = new_viewer_channel
|
||||
|
||||
# Determine new version(s)
|
||||
if new_version:
|
||||
m = version_re.match(new_version)
|
||||
if not m:
|
||||
print "Invalid version string specified!"
|
||||
return -1
|
||||
if update_viewer:
|
||||
viewer_version = new_version
|
||||
else:
|
||||
# Assume we're updating just the build number
|
||||
cl = '%s info "%s"' % (svn, src_root)
|
||||
status, output = _getstatusoutput(cl)
|
||||
if verbose:
|
||||
print
|
||||
print "svn info output:"
|
||||
print "----------------"
|
||||
print output
|
||||
|
||||
branch_match = svn_branch_re.search(output)
|
||||
revision_match = svn_revision_re.search(output)
|
||||
if not branch_match or not revision_match:
|
||||
print "Failed to execute svn info, output follows:"
|
||||
print output
|
||||
return -1
|
||||
branch = branch_match.group(1)
|
||||
revision = revision_match.group(1)
|
||||
if skip_on_branch_re and skip_on_branch_re.match(branch):
|
||||
print "Release Candidate Build, leaving version files untouched."
|
||||
return 0
|
||||
if update_viewer:
|
||||
m = version_re.match(viewer_version)
|
||||
viewer_version = m.group(1)+"."+m.group(2)+"."+m.group(3)+"."+revision
|
||||
|
||||
if verbose:
|
||||
if update_viewer:
|
||||
print "Setting viewer channel/version: '%(viewer_channel)s' / '%(viewer_version)s'" % locals()
|
||||
print
|
||||
|
||||
# split out version parts
|
||||
if viewer_version != None:
|
||||
m = version_re.match(viewer_version)
|
||||
VER_MAJOR = m.group(1)
|
||||
VER_MINOR = m.group(2)
|
||||
VER_PATCH = m.group(3)
|
||||
VER_BUILD = m.group(4)
|
||||
|
||||
# For readability and symmetry with version strings:
|
||||
VIEWER_CHANNEL = viewer_channel
|
||||
|
||||
# Iterate through all of the files in the map, and apply the
|
||||
# substitution filters
|
||||
for filename in re_map.keys():
|
||||
try:
|
||||
# Read the entire file into a string
|
||||
full_fn = src_root + '/' + filename
|
||||
file = open(full_fn,"r")
|
||||
file_str = file.read()
|
||||
file.close()
|
||||
|
||||
if verbose:
|
||||
print "Processing file:",filename
|
||||
for rule in re_map[filename]:
|
||||
repl = rule[1] % locals()
|
||||
file_str = re.sub(rule[0], repl, file_str)
|
||||
|
||||
file = open(full_fn,"w")
|
||||
file.write(file_str)
|
||||
file.close()
|
||||
except IOError:
|
||||
print "File %(filename)s not present, skipping..." % locals()
|
||||
return 0
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user