Compare commits

..

6 Commits

Author SHA1 Message Date
Automatic Release Builder
d3d16d122b new version: 2017.1.2 2017-03-01 20:41:09 +01:00
Automatic Release Builder
16dd404983 Update submodule heads for release/2017.1 2017-02-22 18:08:22 +01:00
Automatic Release Builder
c4f6efe18f There is also no fgpanel icon 2017-02-21 15:23:55 +01:00
Automatic Release Builder
c4af97e4f7 Disable fgpanel for windows due to missing glew dependency 2017-02-21 15:21:58 +01:00
Automatic Release Builder
7431c6253f desperate attempt to get fgdata cloned 2017-02-21 11:28:53 +01:00
Automatic Release Builder
f98d36ab3a set correct release-branch for submodules 2017-02-20 18:52:19 +01:00
50 changed files with 346 additions and 4271 deletions

23
.gitattributes vendored
View File

@@ -1,23 +0,0 @@
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
.gitattributes text export-ignore
.gitignore text export-ignore
*.py text
*.txt text
*.rst text
*.xml text
*.desktop text
*.svg text
*.jpg binary
*.png binary
*.gif binary
*.bat text eol=crlf
AUTHORS text
COPYING text
COPYING.* text
README.* text
Makefile text
ChangeLog text
ChangeLog.* text
/download_and_compile.sh text ident

1
.gitignore vendored
View File

@@ -21,4 +21,3 @@ osgbuild
CMakeCache.txt
*.pyc
build-*
testOutput*

20
.gitmodules vendored
View File

@@ -1,24 +1,20 @@
[submodule "simgear"]
path = simgear
url = ../simgear
branch = next
url = https://git.code.sf.net/p/flightgear/simgear
branch = release/2017.1
[submodule "flightgear"]
path = flightgear
url = ../flightgear
branch = next
url = https://git.code.sf.net/p/flightgear/flightgear
branch = release/2017.1
[submodule "fgrun"]
path = fgrun
url = ../fgrun
url = https://git.code.sf.net/p/flightgear/fgrun
branch = next
[submodule "fgdata"]
path = fgdata
url = ../fgdata
branch = next
url = git://git.code.sf.net/p/flightgear/fgdata
branch = release/2017.1
[submodule "windows-3rd-party"]
path = windows-3rd-party
url = ../windows-3rd-party
url = https://git.code.sf.net/p/flightgear/windows-3rd-party
branch = master
[submodule "getstart"]
path = getstart
url = ../getstart
branch = next

View File

@@ -20,6 +20,13 @@
#include "InstallConfig.iss"
#if GetEnv("VSINSTALLDIR") == ""
#define VSInstallDir "C:\Program Files (x86)\Microsoft Visual Studio 14.0"
#else
#define VSInstallDir GetEnv("VSINSTALLDIR")
#endif
#define VCInstallDir VSInstallDir + "\VC"
#define InstallDir32 "X:\install\msvc140"
#define OSGInstallDir InstallDir32 + "\OpenSceneGraph"
#define OSGPluginsDir OSGInstallDir + "\bin\osgPlugins-" + OSGVersion
@@ -62,7 +69,7 @@ ArchitecturesAllowed=x86 x64
; Sign tool must be defined in the Inno Setup GUI, to avoid
; exposing the certificate password
; SignTool=fg_code_sign1
; SignTool=fg_code_sign1
[Tasks]
; NOTE: The following entry contains English phrases ("Create a desktop icon" and "Additional icons"). You are free to translate them into another language if required.
@@ -85,6 +92,7 @@ Source: "{#ThirdPartyDir}\3rdParty\bin\libintl-8.dll"; DestDir: "{app}\bin"; Che
Source: "{#ThirdPartyDir}\3rdParty\bin\CrashRpt1403.dll"; DestDir: "{app}\bin"; Check: not Is64BitInstallMode
Source: "{#ThirdPartyDir}\3rdParty\bin\crashrpt_lang.ini"; DestDir: "{app}\bin"; Check: not Is64BitInstallMode
Source: "{#ThirdPartyDir}\3rdParty\bin\CrashSender1403.exe"; DestDir: "{app}\bin"; Check: not Is64BitInstallMode
Source: "{#VCInstallDir}\redist\x86\Microsoft.VC140.CRT\*.dll"; DestDir: "{app}\bin"; Check: not Is64BitInstallMode
; 64 bits install
Source: "{#InstallDir64}\bin\*.*"; DestDir: "{app}\bin"; Excludes: "{#ExcludedBinaries}"; Flags: ignoreversion recursesubdirs; Check: Is64BitInstallMode
@@ -99,6 +107,13 @@ Source: "{#ThirdPartyDir}\3rdParty.x64\bin\libintl-8.dll"; DestDir: "{app}\bin";
Source: "{#ThirdPartyDir}\3rdParty.x64\bin\CrashRpt1403.dll"; DestDir: "{app}\bin"; Check: Is64BitInstallMode
Source: "{#ThirdPartyDir}\3rdParty.x64\bin\crashrpt_lang.ini"; DestDir: "{app}\bin"; Check: Is64BitInstallMode
Source: "{#ThirdPartyDir}\3rdParty.x64\bin\CrashSender1403.exe"; DestDir: "{app}\bin"; Check: Is64BitInstallMode
Source: "{#VCInstallDir}\redist\x64\Microsoft.VC140.CRT\*.dll"; DestDir: "{app}\bin"; Check: Is64BitInstallMode
; 32/64 bits install
;NOTE: FGPanel has no 64 bits equivalent, so we are using the 32 bits binary for 32&64 bits OS
;Torsten 2017-02-21: currently no FGPanel due to missing glew dependency
;Source: "{#InstallDir32}\bin\fgpanel.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
; Include the base package
#if IncludeData == "TRUE"
@@ -196,7 +211,7 @@ Name: "{group}\Flightgear Wiki"; Filename: "http://wiki.flightgear.org"
Name: "{group}\Tools\Uninstall FlightGear"; Filename: "{uninstallexe}"
Name: "{group}\Tools\fgjs"; Filename: "cmd"; Parameters: "/k fgjs.exe ""--fg-root={app}\data"""; WorkingDir: "{app}\bin"
Name: "{group}\Tools\yasim"; Filename: "cmd"; Parameters: "/k ""{app}\bin\yasim.exe"" -h"; WorkingDir: "{app}\bin"
Name: "{group}\Tools\fgpanel"; Filename: "cmd"; Parameters: "/k ""{app}\bin\fgpanel.exe"" -h"; WorkingDir: "{app}\bin"
;Name: "{group}\Tools\fgpanel"; Filename: "cmd"; Parameters: "/k ""{app}\bin\fgpanel.exe"" -h"; WorkingDir: "{app}\bin"
Name: "{group}\Tools\FGCom"; Filename: "{app}\bin\fgcom.exe"; WorkingDir: "{app}\bin"
Name: "{group}\Tools\FGCom-testing"; Filename: "{app}\bin\fgcom.exe"; Parameters: "--frequency=910"; WorkingDir: "{app}\bin"
Name: "{group}\Tools\Explore Documentation Folder"; Filename: "{app}\data\Docs"
@@ -319,66 +334,6 @@ begin
end;
end;
var
UninstallCheckCleanPage: TNewNotebookPage;
UninstallBackButton: TNewButton;
UninstallNextButton: TNewButton;
DoCleanCheckbox : TNewCheckBox;
CleanHelp : TNewStaticText;
procedure InitializeUninstallProgressForm();
begin
UninstallProgressForm
UninstallCheckCleanPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallCheckCleanPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallCheckCleanPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallCheckCleanPage.Align := alClient
DoCleanCheckbox := TNewCheckBox.Create(UninstallProgressForm);
DoCleanCheckbox.Parent := UninstallCheckCleanPage;
DoCleanCheckbox.Caption := 'Remove all settings, downloaded scenery and aircraft';
DoCleanCheckbox.Left := ScaleX(10);
DoCleanCheckbox.Top := ScaleY(10);
DoCleanCheckbox.Width := UninstallProgressForm.InnerNotebook.Width - ScaleX(20)
DoCleanCheckbox.Height := ScaleY(30)
CleanHelp := TNewStaticText.Create(UninstallProgressForm);
CleanHelp.Parent := UninstallCheckCleanPage;
CleanHelp.Top := DoCleanCheckbox.Top + DoCleanCheckbox.Height + ScaleY(10);
CleanHelp.Left := DoCleanCheckbox.Left;
CleanHelp.Width := DoCleanCheckbox.Width;
CleanHelp.Height := CleanHelp.AdjustHeight();
CleanHelp.WordWrap := True;
CleanHelp.Caption := 'FlightGear stores some settings in your user folder. In addition, ' +
'scenery or aircraft data may have been downloaded to the download directory. ' +
'To completely remove all these files, select this option.';
UninstallProgressForm.InnerNotebook.ActivePage := UninstallCheckCleanPage;
UninstallNextButton := TNewButton.Create(UninstallProgressForm);
UninstallNextButton.Caption := 'Next';
UninstallNextButton.Parent := UninstallProgressForm;
UninstallNextButton.Left :=
UninstallProgressForm.CancelButton.Left -
UninstallProgressForm.CancelButton.Width -
ScaleX(10);
UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallNextButton.ModalResult := mrOk;
UninstallProgressForm.CancelButton.Enabled := True;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
if UninstallProgressForm.ShowModal = mrCancel then Abort;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
Version: TWindowsVersion;
@@ -404,18 +359,7 @@ begin
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var ResultCode: Integer;
begin
if CurUninstallStep = usUninstall then
begin
if DoCleanCheckbox.Checked = True then
begin
Log('Running clean uninstall');
Exec(ExpandConstant('{app}\bin\fgfs.exe'), '--uninstall', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
Log('clean uninstall completed');
end;
end;
if CurUninstallStep = usPostUninstall then
begin
RemoveFirewallException('FlightGear', ExpandConstant('{app}') + '\bin\fgfs.exe');

View File

@@ -7,10 +7,6 @@ fi
VERSION=`cat flightgear/version`
#####################################################################################
# ensure fgrcc can run when linked against libSimGearCore, for example
export LD_LIBRARY_PATH=$WORKSPACE/dist/lib64:$WORKSPACE/dist/lib:$LD_LIBRARY_PATH
#####################################################################################
# remove old and create fresh build directories
cd $WORKSPACE
@@ -18,6 +14,7 @@ mkdir -p sgBuild
mkdir -p fgBuild
mkdir -p output
rm -rf output/*
rm -rf dist/*
#####################################################################################
echo "Starting on SimGear"
@@ -41,7 +38,7 @@ cp simgear-*.tar.bz2 ../output/.
#####################################################################################
echo "Starting on FlightGear"
cd ../fgBuild
cmake -DCMAKE_INSTALL_PREFIX:PATH=$WORKSPACE/dist -DSIMGEAR_SHARED:BOOL="ON" -DFG_BUILD_TYPE=Release ../flightgear
cmake -DCMAKE_INSTALL_PREFIX:PATH=$WORKSPACE/dist -DSIMGEAR_SHARED:BOOL="ON" ../flightgear
# compile
make
@@ -63,3 +60,4 @@ echo "Assembling base package"
cd $WORKSPACE
tar cjf output/FlightGear-$VERSION-data.tar.bz2 fgdata/

View File

@@ -1,12 +1,5 @@
IF NOT DEFINED WORKSPACE SET WORKSPACE=%~dp0
IF NOT DEFINED IS_NIGHTLY_BUILD SET IS_NIGHTLY_BUILD=1
IF %IS_NIGHTLY_BUILD% EQU 1 (
SET FGBUILDTYPE=Nightly
) ELSE (
SET FGBUILDTYPE=Release
)
REM following are for testing the script locally
REM SET PATH=%PATH%;%ProgramFiles%\CMake\bin;%ProgramFiles(x86)%\"Inno Setup 5"\
@@ -24,7 +17,6 @@ cd build-sg32
cmake ..\simgear -G "Visual Studio 14" ^
-DMSVC_3RDPARTY_ROOT=%WORKSPACE%/windows-3rd-party/msvc140 ^
-DBOOST_ROOT=%WORKSPACE%/windows-3rd-party ^
-DOSG_FSTREAM_EXPORT_FIXED=1 ^
-DCMAKE_PREFIX_PATH:PATH=%OSG32% ^
-DCMAKE_INSTALL_PREFIX:PATH=%WORKSPACE%/install/msvc140
cmake --build . --config RelWithDebInfo --target INSTALL
@@ -35,9 +27,7 @@ cmake ..\flightgear -G "Visual Studio 14" ^
-DCMAKE_INSTALL_PREFIX:PATH=%WORKSPACE%/install/msvc140 ^
-DCMAKE_PREFIX_PATH:PATH=%WORKSPACE%/install/msvc140/OpenSceneGraph ^
-DBOOST_ROOT=%WORKSPACE%/windows-3rd-party ^
-DOSG_FSTREAM_EXPORT_FIXED=1 ^
-DCMAKE_PREFIX_PATH=%QT5SDK32%;%OSG32% ^
-DFG_BUILD_TYPE=%FGBUILDTYPE%
-DCMAKE_PREFIX_PATH=%QT5SDK32%;%OSG32%
cmake --build . --config RelWithDebInfo --target INSTALL
cd ..
@@ -50,7 +40,6 @@ cd build-sg64
cmake ..\SimGear -G "Visual Studio 14 Win64" ^
-DMSVC_3RDPARTY_ROOT=%WORKSPACE%/windows-3rd-party/msvc140 ^
-DBOOST_ROOT=%WORKSPACE%/windows-3rd-party ^
-DOSG_FSTREAM_EXPORT_FIXED=1 ^
-DCMAKE_PREFIX_PATH:PATH=%OSG64% ^
-DCMAKE_INSTALL_PREFIX:PATH=%WORKSPACE%/install/msvc140-64
cmake --build . --config RelWithDebInfo --target INSTALL
@@ -60,17 +49,14 @@ cmake ..\flightgear -G "Visual Studio 14 Win64" ^
-DMSVC_3RDPARTY_ROOT=%WORKSPACE%/windows-3rd-party/msvc140 ^
-DBOOST_ROOT=%WORKSPACE%/windows-3rd-party ^
-DCMAKE_INSTALL_PREFIX:PATH=%WORKSPACE%/install/msvc140-64 ^
-DCMAKE_PREFIX_PATH=%QT5SDK64%;%OSG64% ^
-DOSG_FSTREAM_EXPORT_FIXED=1 ^
-DFG_BUILD_TYPE=%FGBUILDTYPE%
-DCMAKE_PREFIX_PATH=%QT5SDK64%;%OSG64%
cmake --build . --config RelWithDebInfo --target INSTALL
cd ..
REM Qt5 deployment
SET QMLDIR=%WORKSPACE%/flightgear/src/GUI/qml
%QT5SDK32%\bin\windeployqt --release --list target --qmldir %QMLDIR% %WORKSPACE%/install/msvc140/bin/fgfs.exe
%QT5SDK64%\bin\windeployqt --release --list target --qmldir %QMLDIR% %WORKSPACE%/install/msvc140-64/bin/fgfs.exe
%QT5SDK32%\bin\windeployqt --release --list target %WORKSPACE%/install/msvc140/bin/fgfs.exe
%QT5SDK64%\bin\windeployqt --release --list target %WORKSPACE%/install/msvc140-64/bin/fgfs.exe
REM build setup
ECHO Packaging root is %WORKSPACE%
@@ -81,10 +67,9 @@ subst X: %WORKSPACE%.
REM ensure output dir is clean since we upload the entirety of it
rmdir /S /Q output
SET CRASHFIX_UPLOAD_URL=http://crashes.flightgear.org/index.php/debugInfo/uploadExternal
SET FGFS_PDB=src\Main\RelWithDebInfo\fgfs.pdb
ECHO Uploading PDB files to %CRASHFIX_UPLOAD_URL%
upload -v -u %CRASHFIX_UPLOAD_URL% FlightGear %WORKSPACE%\build-fg32\%FGFS_PDB% %WORKSPACE%\build-fg64\%FGFS_PDB%
REM archiving PDB files
copy %WORKSPACE%\build-fg32\src\Main\RelWithDebInfo\fgfs.pdb %WORKSPACE%\Output\fgfs-x86-%BUILD_NUMBER%.pdb
copy %WORKSPACE%\build-fg64\src\Main\RelWithDebInfo\fgfs.pdb %WORKSPACE%\Output\fgfs-x64-%BUILD_NUMBER%.pdb
REM indirect way to get command output into an environment variable
set PATH=%OSG32%\bin;%PATH%

View File

@@ -1,306 +0,0 @@
#!/usr/bin/python
import argparse
import datetime
import lxml.etree as ET
import os
import re
import sgprops
import sys
import catalogTags
CATALOG_VERSION = 4
quiet = False
verbose = False
def warning(msg):
if not quiet:
print(msg)
def log(msg):
if verbose:
print(msg)
# xml node (robust) get text helper
def get_xml_text(e):
if e != None and e.text != None:
return e.text
else:
return ''
# return all available aircraft information from the set file as a
# dict
def scan_set_file(aircraft_dir, set_file, includes):
base_file = os.path.basename(set_file)
base_id = base_file[:-8]
set_path = os.path.join(aircraft_dir, set_file)
includes.append(aircraft_dir)
root_node = sgprops.readProps(set_path, includePaths = includes)
if not root_node.hasChild("sim"):
return None
sim_node = root_node.getChild("sim")
if sim_node == None:
return None
# allow -set.xml files to specifcially exclude themselves from
# the creation process, by setting <exclude-from-catalog>true</>
if (sim_node.getValue("exclude-from-catalog", False) == True):
return None
variant = {}
name = sim_node.getValue("description", None)
if (name == None or len(name) == 0):
warning("Set file " + set_file + " is missing a <description>, skipping")
return None
variant['name'] = name
variant['status'] = sim_node.getValue("status", None)
if sim_node.hasChild('authors'):
# aircraft has structured authors data, handle that
variant['authors'] = extract_authors(sim_node.getChild('authors'))
elif sim_node.hasChild('author'):
variant['author'] = sim_node.getValue("author", None)
if sim_node.hasChild('long-description'):
variant['description'] = sim_node.getValue("long-description", None)
variant['id'] = base_id
# allow -set.xml files to declare themselves as primary.
# we use this avoid needing a variant-of in every other -set.xml
variant['primary-set'] = sim_node.getValue('primary-set', False)
# extract and record previews for each variant
if sim_node.hasChild('previews'):
variant['previews'] = extract_previews(sim_node.getChild('previews'), aircraft_dir)
if sim_node.hasChild('rating'):
rating_node = sim_node.getChild("rating")
variant['rating_FDM'] = rating_node.getValue("FDM", 0)
variant['rating_systems'] = rating_node.getValue("systems", 0)
variant['rating_cockpit'] = rating_node.getValue("cockpit", 0)
variant['rating_model'] = rating_node.getValue("model", 0)
if sim_node.hasChild('tags'):
variant['tags'] = extract_tags(sim_node.getChild('tags'), set_file)
if sim_node.hasChild('thumbnail'):
variant['thumbnail'] = sim_node.getValue("thumbnail", None)
variant['variant-of'] = sim_node.getValue("variant-of", None)
if sim_node.hasChild('minimum-fg-version'):
variant['minimum-fg-version'] = sim_node.getValue('minimum-fg-version', None)
#print ' ', variant
return variant
def extract_previews(previews_node, aircraft_dir):
result = []
for node in previews_node.getChildren("preview"):
previewType = node.getValue("type", None)
previewPath = node.getValue("path", None)
# check path exists in base-name-dir
fullPath = os.path.join(aircraft_dir, previewPath)
if not os.path.isfile(fullPath):
warning("Bad preview path, skipping:" + fullPath)
continue
result.append({'type':previewType, 'path':previewPath})
return result
def extract_tags(tags_node, set_path):
result = []
for node in tags_node.getChildren("tag"):
tag = node.value
# check tag is in the allowed list
if not catalogTags.isValidTag(tag):
warning("Unknown tag value:" + tag + " in " + set_path)
result.append(tag)
return result
def extract_authors(authors_node):
result = []
for author in authors_node.getChildren("author"):
authorName = author.getValue("name", None)
if (authorName == None):
continue
authorNick = author.getValue("nick", None)
authorEmail = author.getValue("email", None)
authorDesc = author.getValue("description", None)
result.append({'name':authorName, 'nick':authorNick, 'email':authorEmail, 'description':authorDesc})
return result
# scan all the -set.xml files in an aircraft directory. Returns a
# package dict and a list of variants.
def scan_aircraft_dir(aircraft_dir, includes):
setDicts = []
primaryAircraft = []
package = None
files = os.listdir(aircraft_dir)
for file in sorted(files, key=lambda s: s.lower()):
if file.endswith('-set.xml'):
# print 'trying:', file
try:
d = scan_set_file(aircraft_dir, file, includes)
if d == None:
continue
except:
print "Skipping set file since couldn't be parsed:", os.path.join(aircraft_dir, file), sys.exc_info()[0]
continue
setDicts.append(d)
if d['primary-set']:
# ensure explicit primary-set aircraft goes first
primaryAircraft.insert(0, d)
elif d['variant-of'] == None:
primaryAircraft.append(d)
# print setDicts
if len(setDicts) == 0:
return None, None
# use the first one
if len(primaryAircraft) == 0:
print "Aircraft has no primary aircraft at all:", aircraft_dir
primaryAircraft = [setDicts[0]]
package = primaryAircraft[0]
if not 'thumbnail' in package:
if (os.path.exists(os.path.join(aircraft_dir, "thumbnail.jpg"))):
package['thumbnail'] = "thumbnail.jpg"
# variants is just all the set dicts except the first one
variants = setDicts
variants.remove(package)
return (package, variants)
# create an xml node with text content
def make_xml_leaf(name, text):
leaf = ET.Element(name)
if text != None:
if isinstance(text, (int, long)):
leaf.text = str(text)
else:
leaf.text = text
else:
leaf.text = ''
return leaf
def append_preview_nodes(node, variant, download_base, package_name):
if not 'previews' in variant:
return
for preview in variant['previews']:
preview_node = ET.Element('preview')
preview_url = download_base + 'previews/' + package_name + '_' + preview['path']
preview_node.append( make_xml_leaf('type', preview['type']) )
preview_node.append( make_xml_leaf('url', preview_url) )
preview_node.append( make_xml_leaf('path', preview['path']) )
node.append(preview_node)
def append_tag_nodes(node, variant):
if not 'tags' in variant:
return
for tag in variant['tags']:
node.append(make_xml_leaf('tag', tag))
def append_author_nodes(node, info):
if 'authors' in info:
authors_node = ET.Element('authors')
for a in info['authors']:
a_node = ET.Element('author')
a_node.append(make_xml_leaf('name', a['name']))
if (a['email'] != None):
a_node.append(make_xml_leaf('email', a['email']))
if (a['nick'] != None):
a_node.append(make_xml_leaf('nick', a['nick']))
if (a['description'] != None):
a_node.append(make_xml_leaf('description', a['description']))
authors_node.append(a_node)
node.append(authors_node)
elif 'author' in info:
# traditional single author string
node.append( make_xml_leaf('author', info['author']) )
def make_aircraft_node(aircraftDirName, package, variants, downloadBase):
#print "package:", package
#print "variants:", variants
package_node = ET.Element('package')
package_node.append( make_xml_leaf('name', package['name']) )
package_node.append( make_xml_leaf('status', package['status']) )
append_author_nodes(package_node, package)
if 'description' in package:
package_node.append( make_xml_leaf('description', package['description']) )
if 'minimum-fg-version' in package:
package_node.append( make_xml_leaf('minimum-fg-version', package['minimum-fg-version']) )
if 'rating_FDM' in package or 'rating_systems' in package \
or 'rating_cockpit' in package or 'rating_model' in package:
rating_node = ET.Element('rating')
package_node.append(rating_node)
rating_node.append( make_xml_leaf('FDM',
package['rating_FDM']) )
rating_node.append( make_xml_leaf('systems',
package['rating_systems']) )
rating_node.append( make_xml_leaf('cockpit',
package['rating_cockpit']) )
rating_node.append( make_xml_leaf('model',
package['rating_model']) )
package_node.append( make_xml_leaf('id', package['id']) )
for variant in variants:
variant_node = ET.Element('variant')
package_node.append(variant_node)
variant_node.append( make_xml_leaf('id', variant['id']) )
variant_node.append( make_xml_leaf('name', variant['name']) )
if 'description' in variant:
variant_node.append( make_xml_leaf('description', variant['description']) )
if 'author' in variant:
variant_node.append( make_xml_leaf('author', variant['author']) )
if 'thumbnail' in variant:
# note here we prefix with the package name, since the thumbnail path
# is assumed to be unique within the package
thumbUrl = downloadBase + "thumbnails/" + aircraftDirName + '_' + variant['thumbnail']
variant_node.append(make_xml_leaf('thumbnail', thumbUrl))
variant_node.append(make_xml_leaf('thumbnail-path', variant['thumbnail']))
variantOf = variant['variant-of']
if variantOf is None:
variant_node.append(make_xml_leaf('variant-of', '_primary_'))
else:
variant_node.append(make_xml_leaf('variant-of', variantOf))
append_preview_nodes(variant_node, variant, downloadBase, aircraftDirName)
append_tag_nodes(variant_node, variant)
append_author_nodes(variant_node, variant)
package_node.append( make_xml_leaf('dir', aircraftDirName) )
download_url = downloadBase + aircraftDirName + '.zip'
package_node.append( make_xml_leaf('url', download_url) )
if 'thumbnail' in package:
thumbnail_url = downloadBase + 'thumbnails/' + aircraftDirName + '_' + package['thumbnail']
package_node.append( make_xml_leaf('thumbnail', thumbnail_url) )
package_node.append( make_xml_leaf('thumbnail-path', package['thumbnail']))
append_preview_nodes(package_node, package, downloadBase, aircraftDirName)
append_tag_nodes(package_node, package)
return package_node

View File

@@ -1,164 +1,80 @@
aircraftTypeTags = [
"aerobatic",
"airship",
"balloon",
"bizjet",
"bomber",
"cargo",
"carrier",
"fighter",
"ga",
"glider",
"groundvehicle",
"helicopter",
"racer",
"spaceship",
"tanker",
"trainer",
"transport",
"ultralight",
"reconnaissance",
"seacraft",
"crop-duster",
"bush-plane"
"ga", "fighter", "helicopter", "glider", "spaceship", "bomber", "groundvehicle",
"tanker", "cargo", "transport", "bizjet", "trainer", "airship", "balloon"
]
manufacturerTags = [
"airbus",
"antonov",
"atr",
"avro",
"bell",
"bleriot",
"boeing",
"bombardier",
"caudron",
"cessna",
"consolidated",
"dassault",
"diamond",
"dornier",
"douglas",
"embraer",
"eurocopter",
"fairchild",
"fairey",
"focke-wulf",
"fokker",
"general-dynamics",
"gotha",
"grumman",
"handley-page",
"hawker",
"heinkel",
"ilyushin",
"junkers",
"lockheed",
"mc-donnell-douglas",
"messerschmitt",
"mikoyan-gurevich",
"mitsubishi",
"north-american",
"northrop",
"pilatus",
"piper",
"republic",
"robin",
"saab",
"short",
"sopwith",
"spad",
"sukhoi",
"supermarine",
"tupolev",
"vickers",
"vought",
"yakovlev"
"boeing", "cessna", "diamond", "douglas", "bell", "piper",
"airbus", "vickers", "lockheed", "fokker",
"embraer", "bombardier", "pilatus", "robin",
"eurocopter"
]
eraTags = [
"1910s",
"early-pioneers",
"ww1",
"1920s",
"1930s",
"1940s",
"golden-age",
"ww2",
"coldwar", "vietnam",
"1950s",
"1960s",
"1970s",
"1980s",
"1990s",
"2000s",
"2010s",
"coldwar",
"early-pioneers",
"golden-age",
"gulfwar1",
"gulfwar2",
"vietnam",
"ww1",
"ww2"
"gulfwar2"
]
featureTags = [
"aerobatic",
"airship",
"amphibious",
"biplane",
"canard",
"castering-wheel",
"delta",
"etops",
"experimental",
"fictional",
"fixed-gear",
"floats",
"glass-cockpit",
"high-wing",
"h-tail",
"hud",
"ifr",
"prototype",
"refuel",
"retractable-gear",
"fixed-gear",
"tail-dragger",
"seaplane",
"skis",
"vtol",
"stol",
"experimental",
"prototype",
"fictional",
"biplane",
"triplane",
"supersonic",
"t-tail",
"tail-dragger",
"tricycle",
"tail-hook",
"triplane",
"v-tail",
"high-wing",
"cannard",
"tail-hook",
"refuel",
"delta",
"variable-geometry",
"vtol",
"wing-fold"
"glass-cockpit",
"hud",
"etops",
"floats",
"amphibious",
"airship",
"aerobatic"
]
propulsionTags = [
"afterburner",
"piston", "radial",
"diesel",
"electric",
"jet",
"propeller",
"piston",
"radial",
"rocket",
"single-engine",
"variable-pitch",
"supercharged",
"turboprop",
"jet", "afterburner", "rocket",
"electric",
"twin-engine",
"variable-pitch",
"fixed-pitch"
"single-engine"
]
simFeatureTags = [
"dual-controls",
"rembrandt",
"tow",
"wildfire"
"dual-controls",
"rembrandt"
]
tags = aircraftTypeTags + manufacturerTags + eraTags + simFeatureTags + propulsionTags + featureTags
def isValidTag(maybeTag):
return maybeTag in tags

View File

@@ -6,13 +6,13 @@
<version n="1">3.5.*</version>
<version n="2">3.6.*</version>
<version n="3">3.7.*</version>
<version n="4">2016.1.*</version>
<version n="5">2016.*.*</version>
<version n="6">2017.*.*</version>
<version n="7">2018.*.*</version>
<id>org.flightgear.fgaddon</id>
<license>GPL</license>
<url>http://mirrors.ibiblio.org/flightgear/ftp/Aircraft/catalog.xml</url>
<name>FlightGear aircraft distribution from fgaddon</name>
<name>FlightGear Aircraft Distribution From fgaddon</name>
<description>This hangar provides aircraft officially supported and maintained by the FlightGear project, under a free-software license.</description>
<de>
<description>Auf Deutsch</description>

View File

@@ -133,8 +133,6 @@ class Node(object):
n.set('type', "bool")
except UnicodeEncodeError:
print "Encoding error with", self._value, type(self._value)
except:
print "Some other exceptiong in sgprops._createXMLElement()"
# index in parent
if (self.index != 0):
@@ -187,13 +185,12 @@ class PropsHandler(handler.ContentHandler):
if 'include' in attrs.keys():
self.handleInclude(attrs['include'])
return
currentState = self._stateStack[-1]
if 'n' in attrs.keys():
try:
index = int(attrs['n'])
except:
print "Invalid index at line:", self._locator.getLineNumber(), "of", self._path
raise IndexError("Invalid index at line:", self._locator.getLineNumber(), "of", self._path)
currentState.recordExplicitIndex(name, index)

View File

@@ -1,8 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList>
<sim>
<name>c150</name>
<description>Cessna 150</description>
</sim>
</PropertyList>

View File

@@ -1,9 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList>
<sim>
<name>c172</name>
<description>Cessna 172P</description>
<author>Wilbur Wright</author>
</sim>
</PropertyList>

View File

@@ -1,24 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList>
<sim include="settings-common.xml">
<authors>
<author n="0">
<name>Wilbur Wright</name>
<email>ww@wright.com</email>
<nick>wilburw</nick>
<description>Model, FDM and cockpit</description>
</author>
<author n="1">
<name>Orville Wright</name>
<description>Testing and systems</description>
</author>
</authors>
<tags>
<tag>fighter</tag>
<tag>1980s</tag>
<tag>glass-cockpit</tag>
</tags>
<minimum-fg-version>2017.4</minimum-fg-version>
</sim>
</PropertyList>

View File

@@ -1,11 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList include="f16-common.xml">
<sim>
<exclude-from-catalog type="bool">true</exclude-from-catalog>
<name>f16-excluded</name>
<description>Fine</description>
<long-description>Blah blah blah</long-description>
<variant-of>f16a</variant-of>
</sim>
</PropertyList>

View File

@@ -1,11 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList include="f16-common.xml">
<sim>
<name>f16-trainer</name>
<description>F16 Trainer</description>
<long-description>Twin-seat trainer version of the F16</long-description>
</sim>
</PropertyList>

View File

@@ -1,36 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList include="f16-common.xml">
<sim>
<name>f16a</name>
<description>F16-A</description>
<long-description>The F16 is compact, light-weight multi-role fighter used around the world</long-description>
<primary-set type="bool">true</primary-set>
<rating>
<FDM type="int">3</FDM>
<systems type="int">1</systems>
<cockpit type="int">2</cockpit>
<model type="int">5</model>
</rating>
<previews>
<preview>
<type>exterior</type>
<splash type="bool">true</splash>
<path>Previews/exterior-1.png</path>
</preview>
<preview>
<type>exterior</type>
<splash type="bool">true</splash>
<path>Previews/exterior-f16a-2.png</path>
</preview>
<preview>
<type>panel</type>
<splash type="bool">false</splash>
<path>Previews/cockpit.png</path>
</preview>
</previews>
</sim>
</PropertyList>

View File

@@ -1,19 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList include="f16-common.xml">
<sim>
<name>f16b</name>
<description>F16-B</description>
<long-description>The F16-B is an upgraded version of the F16A.</long-description>
<variant-of>f16a</variant-of>
<authors n="0">
<author n="0">
<name>James T Kirk</name>
<email>shatner@enterprise.com</email>
<nick>starlover</nick>
<description>Everything</description>
</author>
</authors>
</sim>
</PropertyList>

View File

@@ -1,11 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList include="f16-common.xml">
<sim>
<name>f16broken</name>
<!-- <description></description> -->
<description></description>
<long-description>Blah blah blah</long-description>
<variant-of>f16a</variant-of>
</sim>
</PropertyList>

View File

@@ -1,10 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<PropertyList include="f16-common.xml">
<sim>
<name>f16c</name>
<description>F16-C</description>
<long-description>The F16-C is an upgraded version of the F16A.</long-description>
<variant-of>f16a</variant-of>
</sim>
</PropertyList>

View File

@@ -1,12 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- this file exists to test including + overlaying in the set XML -->
<PropertyList>
<views>
<view n="0">
<foo>dsdddd</foo>
</view>
</views>
</PropertyList>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<sim>
<!-- found in the F-15 XML -->
<uhf n="[0]">
<frequencies>
<selected-mhz type="int">225000</selected-mhz>
</frequencies>
</uhf>
</sim>
</PropertyList>
<uhf n="[0]">
<frequencies>
<selected-mhz type="int">225000</selected-mhz>
</frequencies>
</uhf>
</sim>
</PropertyList>

View File

@@ -1,197 +0,0 @@
#!/usr/bin/python
import unittest
import sgprops
import os
import catalog
import lxml.etree as ET
catalog.quiet = True
class UpdateCatalogTests(unittest.TestCase):
def test_scan_set(self):
info = catalog.scan_set_file("testData/Aircraft/f16", "f16a-set.xml", ["testData/OtherDir"])
self.assertEqual(info['id'], 'f16a')
self.assertEqual(info['name'], 'F16-A')
self.assertEqual(info['primary-set'], True)
self.assertEqual(info['variant-of'], None)
self.assertEqual(info['rating_FDM'], 3)
self.assertEqual(info['rating_model'], 5)
self.assertEqual(len(info['tags']), 3)
self.assertEqual(info['minimum-fg-version'], '2017.4')
authorsArray = info['authors']
self.assertNotIn('author', info)
self.assertEqual(len(authorsArray), 2)
author0 = authorsArray[0]
self.assertEqual(author0['name'], 'Wilbur Wright')
self.assertEqual(author0['nick'], 'wilburw')
self.assertEqual(author0['email'], 'ww@wright.com')
author1 = authorsArray[1]
self.assertEqual(author1['name'], 'Orville Wright')
# self.assertNotIn('nick', author1)
# self.assertNotIn('email', author1)
def test_scan_dir(self):
(pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/f16", ["testData/OtherDir"])
self.assertEqual(pkg['id'], 'f16a')
f16trainer = next(v for v in variants if v['id'] == 'f16-trainer')
self.assertEqual(len(variants), 3)
self.assertEqual(pkg['minimum-fg-version'], '2017.4')
# test variant relatonship between
self.assertEqual(pkg['variant-of'], None)
self.assertEqual(pkg['primary-set'], True)
self.assertEqual(f16trainer['variant-of'], None)
self.assertEqual(f16trainer['primary-set'], False)
f16b = next(v for v in variants if v['id'] == 'f16b')
self.assertEqual(f16b['variant-of'], 'f16a')
self.assertEqual(f16b['primary-set'], False)
authorsArray = f16b['authors']
self.assertNotIn('author', f16b)
self.assertEqual(len(authorsArray), 2)
author0 = authorsArray[0]
self.assertEqual(author0['name'], 'James T Kirk')
self.assertEqual(author0['nick'], 'starlover')
f16c = next(v for v in variants if v['id'] == 'f16c')
self.assertEqual(f16c['variant-of'], 'f16a')
self.assertEqual(f16c['primary-set'], False)
authorsArray = f16c['authors']
self.assertNotIn('author', f16c)
self.assertEqual(len(authorsArray), 2)
# test some older constructs for compat
def test_scan_dir_legacy(self):
(pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/c172", [])
self.assertEqual(pkg['id'], 'c172')
self.assertEqual(pkg['author'], 'Wilbur Wright')
def test_extract_previews(self):
info = catalog.scan_set_file("testData/Aircraft/f16", "f16a-set.xml", ["testData/OtherDir"])
previews = info['previews']
self.assertEqual(len(previews), 3)
self.assertEqual(2, len([p for p in previews if p['type'] == 'exterior']))
self.assertEqual(1, len([p for p in previews if p['type'] == 'panel']))
self.assertEqual(1, len([p for p in previews if p['path'] == 'Previews/exterior-1.png']))
def test_extract_tags(self):
info = catalog.scan_set_file("testData/Aircraft/f16", "f16a-set.xml", ["testData/OtherDir"])
tags = info['tags']
def test_node_creation(self):
(pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/f16", ["testData/OtherDir"])
catalog_node = ET.Element('PropertyList')
catalog_root = ET.ElementTree(catalog_node)
pkgNode = catalog.make_aircraft_node('f16', pkg, variants, "http://foo.com/testOutput/")
catalog_node.append(pkgNode)
# write out so we can parse using sgprops
# yes we are round-tripping via the disk, if you can improve
# then feel free..
if not os.path.isdir("testOutput"):
os.mkdir("testOutput")
cat_file = os.path.join("testOutput", 'catalog_fragment.xml')
catalog_root.write(cat_file, encoding='utf-8', xml_declaration=True, pretty_print=True)
parsed = sgprops.readProps(cat_file)
parsedPkgNode = parsed.getChild("package")
self.assertEqual(parsedPkgNode.name, "package");
self.assertEqual(parsedPkgNode.getValue('id'), pkg['id']);
self.assertEqual(parsedPkgNode.getValue('dir'), 'f16');
self.assertEqual(parsedPkgNode.getValue('url'), 'http://foo.com/testOutput/f16.zip');
self.assertEqual(parsedPkgNode.getValue('thumbnail'), 'http://foo.com/testOutput/thumbnails/f16_thumbnail.jpg');
self.assertEqual(parsedPkgNode.getValue('thumbnail-path'), 'thumbnail.jpg');
self.assertEqual(parsedPkgNode.getValue('name'), pkg['name']);
self.assertEqual(parsedPkgNode.getValue('description'), pkg['description']);
self.assertEqual(parsedPkgNode.getValue('minimum-fg-version'), "2017.4");
parsedVariants = parsedPkgNode.getChildren("variant")
self.assertEqual(len(parsedVariants), 3)
# author data verification
self.assertFalse(parsedPkgNode.hasChild('author'));
parsedAuthors = parsedPkgNode.getChild("authors").getChildren('author')
self.assertEqual(len(parsedAuthors), 2)
author1 = parsedAuthors[0]
self.assertEqual(author1.getValue("name"), "Wilbur Wright")
self.assertEqual(author1.getValue("nick"), "wilburw")
self.assertEqual(author1.getValue("email"), "ww@wright.com")
author2 = parsedAuthors[1]
self.assertEqual(author2.getValue("name"), "Orville Wright")
f16ANode = parsedPkgNode
self.assertEqual(f16ANode.getValue('name'), 'F16-A');
for index, pv in enumerate(parsedVariants):
var = variants[index]
self.assertEqual(pv.getValue('name'), var['name']);
self.assertEqual(pv.getValue('description'), var['description']);
if (var['id'] == 'f16-trainer'):
self.assertEqual(pv.getValue('variant-of'), '_primary_')
# self.assertEqual(pv.getValue('author'), "Wilbur Wright");
elif (var['id'] == 'f16b'):
self.assertEqual(pv.getValue('variant-of'), 'f16a')
self.assertEqual(pv.getValue('description'), 'The F16-B is an upgraded version of the F16A.')
# variant author verification
parsedAuthors = pv.getChild("authors").getChildren('author')
author1 = parsedAuthors[0]
self.assertEqual(author1.getValue("name"), "James T Kirk")
self.assertEqual(author1.getValue("nick"), "starlover")
self.assertEqual(author1.getValue("email"), "shatner@enterprise.com")
self.assertEqual(author1.getValue("description"), "Everything")
def test_minimalAircraft(self):
# test an aircraft with a deliberately spartan -set.xml file with
# most interesting data missing
(pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/c150", ["testData/OtherDir"])
catalog_node = ET.Element('PropertyList')
catalog_root = ET.ElementTree(catalog_node)
pkgNode = catalog.make_aircraft_node('c150', pkg, variants, "http://foo.com/testOutput/")
catalog_node.append(pkgNode)
if not os.path.isdir("testOutput2"):
os.mkdir("testOutput2")
cat_file = os.path.join("testOutput2", 'catalog_fragment.xml')
catalog_root.write(cat_file, encoding='utf-8', xml_declaration=True, pretty_print=True)
parsed = sgprops.readProps(cat_file)
parsedPkgNode = parsed.getChild("package")
self.assertEqual(parsedPkgNode.getValue('id'), pkg['id'])
self.assertEqual(parsedPkgNode.getValue('dir'), 'c150')
self.assertEqual(parsedPkgNode.getValue('url'), 'http://foo.com/testOutput/c150.zip')
self.assertFalse(parsedPkgNode.hasChild('thumbnail'))
self.assertFalse(parsedPkgNode.hasChild('thumbnail-path'));
self.assertEqual(parsedPkgNode.getValue('name'), pkg['name']);
self.assertFalse(parsedPkgNode.hasChild('description'));
self.assertFalse(parsedPkgNode.hasChild('author'));
self.assertFalse(parsedPkgNode.hasChild('minimum-fg-version'));
self.assertFalse(parsedPkgNode.hasChild('variant'));
if __name__ == '__main__':
unittest.main()

View File

@@ -11,8 +11,6 @@ import subprocess
import time
import sgprops
import sys
import catalogTags
import catalog
CATALOG_VERSION = 4
@@ -36,6 +34,127 @@ def get_xml_text(e):
else:
return ''
# create an xml node with text content
def make_xml_leaf(name, text):
leaf = ET.Element(name)
if text != None:
if isinstance(text, (int, long)):
leaf.text = str(text)
else:
leaf.text = text
else:
leaf.text = ''
return leaf
# return all available aircraft information from the set file as a
# dict
def scan_set_file(aircraft_dir, set_file):
global includes
base_file = os.path.basename(set_file)
base_id = base_file[:-8]
set_path = os.path.join(aircraft_dir, set_file)
local_includes = includes
local_includes.append(aircraft_dir)
root_node = sgprops.readProps(set_path, includePaths = local_includes)
if not root_node.hasChild("sim"):
return None
sim_node = root_node.getChild("sim")
if sim_node == None:
return None
variant = {}
variant['name'] = sim_node.getValue("description", None)
variant['status'] = sim_node.getValue("status", None)
variant['author'] = sim_node.getValue("author", None)
variant['description'] = sim_node.getValue("long-description", None)
variant['id'] = base_id
# allow -set.xml files to declare themselves as primary.
# we use this avoid needing a variant-of in every other -set.xml
variant['primary-set'] = sim_node.getValue('primary-set', False)
# extract and record previews for each variant
if sim_node.hasChild('previews'):
print "has previews ..."
variant['previews'] = extract_previews(sim_node.getChild('previews'), aircraft_dir)
if sim_node.hasChild('rating'):
rating_node = sim_node.getChild("rating")
variant['rating_FDM'] = rating_node.getValue("FDM", 0)
variant['rating_systems'] = rating_node.getValue("systems", 0)
variant['rating_cockpit'] = rating_node.getValue("cockpit", 0)
variant['rating_model'] = rating_node.getValue("model", 0)
variant['variant-of'] = sim_node.getValue("variant-of", None)
#print ' ', variant
return variant
def extract_previews(previews_node, aircraft_dir):
result = []
for node in previews_node.getChildren("preview"):
previewType = node.getValue("type", None)
previewPath = node.getValue("path", None)
# check path exists in base-name-dir
fullPath = os.path.join(aircraft_dir, previewPath)
if not os.path.isfile(fullPath):
print "Bad preview path, skipping:" + fullPath
continue
result.append({'type':previewType, 'path':previewPath})
return result
# scan all the -set.xml files in an aircraft directory. Returns a
# package dict and a list of variants.
def scan_aircraft_dir(aircraft_dir):
# old way of finding the master aircraft: it's the only one whose
# variant-of is empty. All the others have an actual value
# newer alternative is to specify one -set.xml as the primary. All the
# others are therefore variants.
setDicts = []
found_master = False
package = None
files = os.listdir(aircraft_dir)
for file in sorted(files, key=lambda s: s.lower()):
if file.endswith('-set.xml'):
try:
d = scan_set_file(aircraft_dir, file)
if d == None:
continue
except:
print "Skipping set file since couldn't be parsed:", os.path.join(aircraft_dir, file), sys.exc_info()[0]
continue
setDicts.append(d)
if d['primary-set']:
found_master = True
package = d
# didn't find a dict identified explicitly as the primary, look for one
# with an undefined variant-of
if not found_master:
for d in setDicts:
if d['variant-of'] == '':
found_master = True
package = d
break
if not found_master:
if len(setDicts) > 1:
print "Warning, no explicit primary set.xml in " + aircraft_dir
# use the first one
package = setDicts[0]
# variants is just all the set dicts except the master
variants = setDicts
variants.remove(package)
return (package, variants)
# use svn commands to report the last change date within dir
def last_change_date_svn(dir):
command = [ 'svn', 'info', dir ]
@@ -84,6 +203,18 @@ def get_md5sum(file):
f.close()
return md5sum
def append_preview_nodes(node, variant, download_base, package_name):
if not 'previews' in variant:
return
for preview in variant['previews']:
preview_node = ET.Element('preview')
preview_url = download_base + 'previews/' + package_name + '_' + preview['path']
preview_node.append( make_xml_leaf('type', preview['type']) )
preview_node.append( make_xml_leaf('url', preview_url) )
preview_node.append( make_xml_leaf('path', preview['path']) )
node.append(preview_node)
def copy_previews_for_variant(variant, package_name, package_dir, previews_dir):
if not 'previews' in variant:
return
@@ -103,98 +234,6 @@ def copy_previews_for_package(package, variants, package_name, package_dir, prev
for v in variants:
copy_previews_for_variant(v, package_name, package_dir, previews_dir)
def copy_thumbnail_for_variant(variant, package_name, package_dir, thumbnails_dir):
if not 'thumbnail' in variant:
return
thumb_src = os.path.join(package_dir, variant['thumbnail'])
thumb_dst = os.path.join(thumbnails_dir, package_name + '_' + variant['thumbnail'])
dir = os.path.dirname(thumb_dst)
if not os.path.isdir(dir):
os.makedirs(dir)
if os.path.exists(thumb_src):
shutil.copy2(thumb_src, thumb_dst)
def copy_thumbnails_for_package(package, variants, package_name, package_dir, thumbnails_dir):
copy_thumbnail_for_variant(package, package_name, package_dir, thumbnails_dir)
# and now each variant in turn
for v in variants:
copy_thumbnail_for_variant(v, package_name, package_dir, thumbnails_dir)
def process_aircraft_dir(name, repo_path):
global includes
global download_base
global output_dir
global valid_zips
global previews_dir
aircraft_dir = os.path.join(repo_path, name)
if not os.path.isdir(aircraft_dir):
return
(package, variants) = catalog.scan_aircraft_dir(aircraft_dir, includes)
if package == None:
print "skipping:", name, "(no -set.xml files)"
return
print "%s:" % name,
package_node = catalog.make_aircraft_node(name, package, variants, download_base)
download_url = download_base + name + '.zip'
if 'thumbnail' in package:
# this is never even used, but breaks the script by assuming
# all aircraft packages have thumbnails defined?
thumbnail_url = download_base + 'thumbnails/' + name + '_' + package['thumbnail']
# get cached md5sum if it exists
md5sum = get_xml_text(md5sum_root.find(str('aircraft_' + name)))
# now do the packaging and rev number stuff
dir_mtime = scan_dir_for_change_date_mtime(aircraft_dir)
if repo_type == 'svn':
rev = last_change_date_svn(aircraft_dir)
else:
d = datetime.datetime.utcfromtimestamp(dir_mtime)
rev = d.strftime("%Y%m%d")
package_node.append( catalog.make_xml_leaf('revision', rev) )
#print "rev:", rev
#print "dir mtime:", dir_mtime
zipfile = os.path.join( output_dir, name + '.zip' )
valid_zips.append(name + '.zip')
if not os.path.exists(zipfile) \
or dir_mtime > os.path.getmtime(zipfile) \
or args.clean:
# rebuild zip file
print "updating:", zipfile
make_aircraft_zip(repo_path, name, zipfile)
md5sum = get_md5sum(zipfile)
else:
print "(no change)"
if md5sum == "":
md5sum = get_md5sum(zipfile)
filesize = os.path.getsize(zipfile)
package_node.append( catalog.make_xml_leaf('md5', md5sum) )
package_node.append( catalog.make_xml_leaf('file-size-bytes', filesize) )
# handle md5sum cache
node = md5sum_root.find('aircraft_' + name)
if node != None:
node.text = md5sum
else:
md5sum_root.append( catalog.make_xml_leaf('aircraft_' + name, md5sum) )
# handle thumbnails
copy_thumbnails_for_package(package, variants, name, aircraft_dir, thumbnail_dir)
catalog_node.append(package_node)
# copy previews for the package and variants into the
# output directory
copy_previews_for_package(package, variants, name, aircraft_dir, previews_dir)
#def get_file_stats(file):
# f = open(file, 'r')
# md5 = hashlib.md5(f.read()).hexdigest()
@@ -227,9 +266,6 @@ else:
scm_list = config_node.findall('scm')
upload_node = config_node.find('upload')
download_base = get_xml_text(config_node.find('download-url'))
if not download_base.endswith('/'):
download_base += '/'
output_dir = get_xml_text(config_node.find('local-output'))
if output_dir == '':
output_dir = os.path.join(args.dir, 'output')
@@ -303,9 +339,106 @@ for scm in scm_list:
print "skipping:", name
continue
# process each aircraft in turn
# print name, repo_path
process_aircraft_dir(name, repo_path)
aircraft_dir = os.path.join(repo_path, name)
if os.path.isdir(aircraft_dir):
print "%s:" % name,
(package, variants) = scan_aircraft_dir(aircraft_dir)
if package == None:
print "skipping:", name, "(no -set.xml files)"
continue
#print "package:", package
#print "variants:", variants
package_node = ET.Element('package')
package_node.append( make_xml_leaf('name', package['name']) )
package_node.append( make_xml_leaf('status', package['status']) )
package_node.append( make_xml_leaf('author', package['author']) )
package_node.append( make_xml_leaf('description', package['description']) )
if 'rating_FDM' in package or 'rating_systems' in package \
or 'rating_cockpit' in package or 'rating_model' in package:
rating_node = ET.Element('rating')
package_node.append(rating_node)
rating_node.append( make_xml_leaf('FDM',
package['rating_FDM']) )
rating_node.append( make_xml_leaf('systems',
package['rating_systems']) )
rating_node.append( make_xml_leaf('cockpit',
package['rating_cockpit']) )
rating_node.append( make_xml_leaf('model',
package['rating_model']) )
package_node.append( make_xml_leaf('id', package['id']) )
for variant in variants:
variant_node = ET.Element('variant')
package_node.append(variant_node)
variant_node.append( make_xml_leaf('id', variant['id']) )
variant_node.append( make_xml_leaf('name', variant['name']) )
if 'description' in variant:
variant_node.append( make_xml_leaf('description', variant['description']) )
if 'author' in variant:
variant_node.append( make_xml_leaf('author', variant['author']) )
append_preview_nodes(variant_node, variant, download_base, name)
package_node.append( make_xml_leaf('dir', name) )
if not download_base.endswith('/'):
download_base += '/'
download_url = download_base + name + '.zip'
thumbnail_url = download_base + 'thumbnails/' + name + '_thumbnail.jpg'
package_node.append( make_xml_leaf('url', download_url) )
package_node.append( make_xml_leaf('thumbnail', thumbnail_url) )
append_preview_nodes(package_node, package, download_base, name)
# todo: url (download), thumbnail (download url)
# get cached md5sum if it exists
md5sum = get_xml_text(md5sum_root.find(str('aircraft_' + name)))
# now do the packaging and rev number stuff
dir_mtime = scan_dir_for_change_date_mtime(aircraft_dir)
if repo_type == 'svn':
rev = last_change_date_svn(aircraft_dir)
else:
d = datetime.datetime.utcfromtimestamp(dir_mtime)
rev = d.strftime("%Y%m%d")
package_node.append( make_xml_leaf('revision', rev) )
#print "rev:", rev
#print "dir mtime:", dir_mtime
zipfile = os.path.join( output_dir, name + '.zip' )
valid_zips.append(name + '.zip')
if not os.path.exists(zipfile) \
or dir_mtime > os.path.getmtime(zipfile) \
or args.clean:
# rebuild zip file
print "updating:", zipfile
make_aircraft_zip(repo_path, name, zipfile)
md5sum = get_md5sum(zipfile)
else:
print "(no change)"
if md5sum == "":
md5sum = get_md5sum(zipfile)
filesize = os.path.getsize(zipfile)
package_node.append( make_xml_leaf('md5', md5sum) )
package_node.append( make_xml_leaf('file-size-bytes', filesize) )
# handle md5sum cache
node = md5sum_root.find('aircraft_' + name)
if node != None:
node.text = md5sum
else:
md5sum_root.append( make_xml_leaf('aircraft_' + name, md5sum) )
# handle thumbnails
thumbnail_src = os.path.join(aircraft_dir, 'thumbnail.jpg')
thumbnail_dst = os.path.join(thumbnail_dir, name + '_thumbnail.jpg')
if os.path.exists(thumbnail_src):
shutil.copy2(thumbnail_src, thumbnail_dst)
catalog_node.append(package_node)
package_node.append( make_xml_leaf('thumbnail-path', 'thumbnail.jpg') )
# copy previews for the package and variants into the
# output directory
copy_previews_for_package(package, variants, name, aircraft_dir, previews_dir)
# write out the master catalog file
cat_file = os.path.join(output_dir, 'catalog.xml')

View File

@@ -17,16 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
script_blob_id='$Id$'
# Slightly tricky substitution to avoid our regexp being wildly replaced with
# the blob name (id) when the script is checked out:
#
# First extract the hexadecimal blob object name followed by a '$'
VERSION="$(echo "$script_blob_id" | sed 's@\$Id: *\([0-9a-f]\+\) *@\1@')"
# Then remove the trailing '$'
VERSION="${VERSION%\$}"
PROGNAME=$(basename "$0")
VERSION="2.34"
FGVERSION="release/$(git ls-remote --heads https://git.code.sf.net/p/flightgear/flightgear|grep '\/release\/'|cut -f4 -d'/'|sort -t . -k 1,1n -k2,2n -k3,3n|tail -1)"
#######################################################
@@ -55,8 +46,6 @@ BUILD_TYPE="RelWithDebInfo"
SG_CMAKEARGS=""
FG_CMAKEARGS=""
declare -a UNMATCHED_OPTIONAL_PKG_ALTERNATIVES
while getopts "shc:p:a:d:r:j:O:ib:" OPTION; do
case $OPTION in
s) STABLE="STABLE" ;;
@@ -111,16 +100,6 @@ function _logSep(){
echo "***********************************" >> $LOGFILE
}
function _aptUpdate(){
echo "Asking password for 'apt-get update'..."
sudo apt-get update
}
function _aptInstall(){
echo "Asking password for 'apt-get install $*'..."
sudo apt-get install "$@"
}
function _gitUpdate(){
if [ "$DOWNLOAD" != "y" ]; then
return
@@ -164,90 +143,6 @@ function _make(){
fi
}
# Add an available, non-virtual package matching one of the given regexps.
#
# Each positional parameter is interpreted as a POSIX extended regular
# expression. These parameters are examined from left to right, and the first
# available matching package is added to the global PKG variable. If no match
# is found, the script aborts.
function _mandatory_pkg_alternative(){
local pkg
if [[ $# -lt 1 ]]; then
echo "Empty package alternative: this is a bug in the script, aborting." \
| tee -a "$LOGFILE"
exit 1
fi
echo "Considering a package alternative:" "$@" | tee -a "$LOGFILE"
pkg=$(_find_package_alternative "$@")
if [[ -n "$pkg" ]]; then
echo "Package alternative matched for $pkg" | tee -a "$LOGFILE"
PKG="$PKG $pkg"
else
echo "No match found for the package alternative, aborting." \
| tee -a "$LOGFILE"
exit 1
fi
return 0
}
# If available, add a non-virtual package matching one of the given regexps.
#
# Returning 0 or 1 on success to indicate whether a match was found could be
# done, but would need to be specifically handled at the calling site,
# since the script is run under 'set -e' regime.
function _optional_pkg_alternative(){
local pkg
if [[ $# -lt 1 ]]; then
echo "Empty optional package alternative: this is a bug in the script," \
"aborting." | tee -a "$LOGFILE"
exit 1
fi
echo "Considering an optional package alternative:" "$@" | tee -a "$LOGFILE"
pkg=$(_find_package_alternative "$@")
if [[ -n "$pkg" ]]; then
echo "Optional package alternative matched for $pkg" | tee -a "$LOGFILE"
PKG="$PKG $pkg"
else
echo "No match found for the optional package alternative, continuing" \
"anyway." | tee -a "$LOGFILE"
# "$*" so that we only add one element to the array in this line
UNMATCHED_OPTIONAL_PKG_ALTERNATIVES+=("$*")
fi
return 0
}
# This function requires the 'dctrl-tools' package
function _find_package_alternative(){
local pkg
if [[ $# -lt 1 ]]; then
return 0 # Nothing could be found
fi
# This finds non-virtual packages only (on purpose)
pkg="$(apt-cache dumpavail | \
grep-dctrl -e -sPackage -FPackage \
"^[[:space:]]*($1)[[:space:]]*\$" - | \
sed -ne '1s/^Package:[[:space:]]*//gp')"
if [[ -n "$pkg" ]]; then
echo "$pkg"
return 0
else
# Try with the next regexp
shift
_find_package_alternative "$@"
fi
}
#######################################################
# set script to stop if an error occours
set -e
@@ -306,47 +201,18 @@ _logSep
#######################################################
#######################################################
if [[ "$DOWNLOAD_PACKAGES" = "y" ]] && [[ "$APT_GET_UPDATE" = "y" ]]; then
_aptUpdate
fi
# Ensure 'dctrl-tools' is installed
if [[ "$(dpkg-query --showformat='${Status}\n' --show dctrl-tools \
2>/dev/null | awk '{print $3}')" != "installed" ]]; then
if [[ "$DOWNLOAD_PACKAGES" = "y" ]]; then
_aptInstall dctrl-tools
else
echo -n "The 'dctrl-tools' package is needed, but DOWNLOAD_PACKAGES is "
echo -e "not set to 'y'.\nAborting."
exit 1
fi
fi
# Minimum
PKG="build-essential cmake git"
_mandatory_pkg_alternative libcurl4-openssl-dev libcurl4-gnutls-dev
# cmake
PKG="$PKG libarchive-dev libbz2-dev libexpat1-dev libjsoncpp-dev liblzma-dev libncurses5-dev procps zlib1g-dev"
PKG="$PKG libarchive-dev libbz2-dev libcurl4-gnutls-dev libexpat1-dev libjsoncpp-dev liblzma-dev libncurses5-dev procps zlib1g-dev"
# TG
PKG="$PKG libcgal-dev libgdal-dev libtiff5-dev"
# TGGUI/OpenRTI
PKG="$PKG libqt4-dev"
# SG/FG
PKG="$PKG zlib1g-dev freeglut3-dev libboost-dev"
_mandatory_pkg_alternative libopenscenegraph-3.4-dev libopenscenegraph-dev \
'libopenscenegraph-[0-9]+\.[0-9]+-dev'
PKG="$PKG zlib1g-dev freeglut3-dev libboost-dev libopenscenegraph-dev"
# FG
PKG="$PKG libopenal-dev libudev-dev qt5-default qtdeclarative5-dev libdbus-1-dev libplib-dev"
_mandatory_pkg_alternative libpng-dev libpng12-dev libpng16-dev
# The following packages are needed for the built-in launcher
_optional_pkg_alternative qml-module-qtquick2
_optional_pkg_alternative qml-module-qtquick-window2
_optional_pkg_alternative qml-module-qtquick-dialogs
# The following packages are only needed for the Qt-based remote Canvas
# (comment written at the time of FG 2018.2).
_optional_pkg_alternative qtbase5-private-dev
_optional_pkg_alternative qtdeclarative5-private-dev
PKG="$PKG libopenal-dev libudev-dev qt5-default libdbus-1-dev libpng12-dev libplib-dev"
# FGPanel
PKG="$PKG fluid libbz2-dev libfltk1.3-dev libxi-dev libxmu-dev"
# FGAdmin
@@ -358,8 +224,12 @@ PKG="$PKG python-tk"
# FGx (FGx is not compatible with Qt5, however we have installed Qt5 by default)
#PKG="$PKG libqt5xmlpatterns5-dev libqt5webkit5-dev"
if [[ "$DOWNLOAD_PACKAGES" = "y" ]]; then
_aptInstall $PKG
if [ "$DOWNLOAD_PACKAGES" = "y" ]; then
echo "Asking password for apt-get operations..."
if [ "$APT_GET_UPDATE" = "y" ]; then
sudo apt-get update
fi
sudo apt-get install $PKG
fi
#######################################################
@@ -509,8 +379,8 @@ if [[ "$(declare -p WHATTOBUILD)" =~ '['([0-9]+)']="OSG"' ]]; then
echo "****************************************" | tee -a $LOGFILE
cd "$CBD"/openscenegraph
_gitDownload https://github.com/openscenegraph/osg.git
_gitUpdate OpenSceneGraph-3.4
_gitUpdate OpenSceneGraph-3.2
if [ "$RECONFIGURE" = "y" ]; then
cd "$CBD"
mkdir -p build/openscenegraph
@@ -917,24 +787,6 @@ if [[ "$(declare -p WHATTOBUILD)" =~ '['([0-9]+)']="TERRAGEARGUI"' ]]; then
echo "./TerraGUI \$@" >> run_terrageargui.sh
fi
# Print optional package alternatives that didn't match (this helps with
# troubleshooting)
if [[ ${#UNMATCHED_OPTIONAL_PKG_ALTERNATIVES[@]} -gt 0 ]]; then
echo | tee -a "$LOGFILE"
printf "The following optional package alternative(s) didn't match:\n\n" \
| tee -a "$LOGFILE"
for alt in "${UNMATCHED_OPTIONAL_PKG_ALTERNATIVES[@]}"; do
printf " %s\n" "$alt" | tee -a "$LOGFILE"
done
printf "\nThis could explain missing optional features in FlightGear or \
other software\ninstalled by $PROGNAME.\n" | tee -a "$LOGFILE"
else
printf "All optional package alternatives have found a matching package.\n" \
| tee -a "$LOGFILE"
fi
echo ""
echo "download_and_compile.sh has finished to work"

2
fgdata

Submodule fgdata updated: b6df0ded76...eedff86003

Submodule getstart deleted from 7b216d458f

View File

@@ -21,7 +21,7 @@ echo "Build path is: $PATH"
###############################################################################
echo "Starting on SimGear"
pushd sgBuild
cmake -DCMAKE_INSTALL_PREFIX:PATH=$WORKSPACE/dist -DCMAKE_BUILD_TYPE=RelWithDebInfo ../simgear
cmake -DCMAKE_INSTALL_PREFIX:PATH=$WORKSPACE/dist -DENABLE_CURL:BOOL="ON" -DCMAKE_BUILD_TYPE=RelWithDebInfo ../simgear
# compile
make
@@ -39,14 +39,7 @@ popd
################################################################################
echo "Starting on FlightGear"
pushd fgBuild
if [ $FG_IS_RELEASE == '1' ]; then
FGBUILDTYPE=Release
else
FGBUILDTYPE=Nightly
fi
cmake -DFG_BUILD_TYPE=$FGBUILDTYPE -DCMAKE_INSTALL_PREFIX:PATH=$WORKSPACE/dist -DCMAKE_BUILD_TYPE=RelWithDebInfo ../flightgear
cmake -DFG_NIGHTLY=1 -DCMAKE_INSTALL_PREFIX:PATH=$WORKSPACE/dist -DCMAKE_BUILD_TYPE=RelWithDebInfo ../flightgear
make

View File

@@ -40,7 +40,6 @@ puts "Is-release? : ##{$isRelease}"
$prefixDir=Dir.pwd + "/dist"
dmgDir=Dir.pwd + "/image"
srcDir=Dir.pwd + "/flightgear"
qmlDir=srcDir + "/src/GUI/qml"
puts "Erasing previous image dir"
`rm -rf #{dmgDir}`
@@ -49,10 +48,8 @@ bundle=dmgDir + "/FlightGear.app"
# run macdeployt before we rename the bundle, otherwise it
# can't find the bundle executable
# also note if adding options here, the bundle path has to be
# the first argument to macdeployqt
puts "Running macdeployqt on the bundle to copy Qt libraries"
`macdeployqt #{$prefixDir}/fgfs.app -qmldir=#{qmlDir}`
`macdeployqt #{$prefixDir}/fgfs.app`
puts "Moving & renaming app bundle"
`mkdir -p #{dmgDir}`

View File

@@ -1,126 +0,0 @@
-*- coding: utf-8 -*-
Quick start for the localization (l10n) scripts
===============================================
The following assumes that all of these are in present in
$FG_ROOT/Translations:
- the default translation (default/*.xml);
- the legacy FlightGear XML localization files (<language_code>/*.xml);
- except for 'fg-convert-translation-files' which creates them, existing
XLIFF 1.2 files (<language_code>/FlightGear-nonQt.xlf).
Note: the legacy FlightGear XML localization files are only needed by
'fg-convert-translation-files' when migrating to the XLIFF format. The
other scripts only need the default translation and obviously, for
'fg-update-translation-files', the current XLIFF files[1].
Creating XLIFF files from existing FlightGear legacy XML translation files
--------------------------------------------------------------------------
To get the initial XLIFF files (generated from the default translation in
$FG_ROOT/Translations/default as well as the legacy FlightGear XML
localization files in $FG_ROOT/Translations/<language_code>):
languages="de en_US es fr it nl pl pt zh_CN"
# Your shell must expand $languages as several words for the following
# commands to work. POSIX shell does that, Bash too apparently, but not Zsh
# (by default). In Zsh, you can use $=languages or ${=languages} to ensure
# the expansion uses word splitting.
fg-convert-translation-files --transl-dir="$FG_ROOT/Translations" $languages
# Add strings found in the default translation but missing in the legacy FG
# XML l10n files
fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \
merge-new-master $languages
Updating XLIFF files to reflect changes in the default translation
------------------------------------------------------------------
When master strings[2] have changed (in a large sense, i.e.: strings added,
modified or removed, or categories added or removed[3]):
fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \
merge-new-master $languages
Updating XLIFF files to mark or remove obsolete translated strings
------------------------------------------------------------------
To remove unused translated strings (not to be done too often in my opinion):
fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \
remove-unused $languages
(you may replace 'remove-unused' with 'mark-unused' to just mark the strings
as not-to-be-translated, however 'merge-new-master' presented above already
does that)
Merging contents from an XLIFF file into another one
----------------------------------------------------
Suppose a translator has been working on a particular translation file, and
meanwhile the official XLIFF file for this translation has been updated in
FGData (new translatable strings added, obsolete strings marked or removed,
etc.). In such a case, 'fg-merge-xliff-into-xliff' can be used to merge the
translator's work into the project file. Essentially, this means that for all
strings that have the same source text, plural status, number of plural forms
and of course target language, the target texts, “approved” status and
translator comments will be taken from the first file passed in the following
command:
fg-merge-xliff-into-xliff TRANSLATOR_FILE PROJECT_FILE
Used like this, PROJECT_FILE will be updated with data from TRANSLATOR_FILE.
If you don't want to modify PROJECT_FILE, use the -o (--output) option. If '-'
is passed as argument to this option, then the result is written to the
standard output.
Creating skeleton XLIFF files for new translations
--------------------------------------------------
To create skeleton translations for new languages (e.g., for fr_BE, en_AU and
ca):
1) Check (add if necessary) that flightgear/meta/i18n.py knows the plural
forms used in the new languages. This is done by editing PLURAL_FORMS
towards the top of this i18n.py file (very easy). If the existing entry
for, e.g., "zh" is sufficient for zh_TW or zh_HK, just let "zh" handle
them: it will be tried as fallback if there is no perfect match on
language and territory.
2) Run a command such as:
fg-new-translations --transl-dir="$FG_ROOT/Translations" fr_BE en_AU ca
(if you do this for only one language at a time, you can use the -o
option to precisely control where the output goes, otherwise
fg-new-translations chooses an appropriate place based on the value
specified for --transl-dir)
Getting more information on the scripts
---------------------------------------
fg-convert-translation-files, fg-update-translation-files,
fg-merge-xliff-into-xliff and fg-new-translations all support the --help
option for more detailed information.
Footnotes
---------
[1] Except for the fg-merge-xliff-into-xliff script, which doesn't have any
of these requirements.
[2] Strings in the default translation.
[3] Only empty categories are removed by this command. An obsolete category
can be made empty by manual editing (easy, just locate the right
<group>) or this way:
fg-update-translation-files --transl-dir=... mark-unused
fg-update-translation-files --transl-dir=... remove-unused
(note that this will remove *all* strings marked as unused in the first
step, not only those in some particular category!)

View File

@@ -1,181 +0,0 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# fg-convert-translation-files --- Convert FlightGear's translation files
# Copyright (C) 2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import argparse
import collections
import locale
import os
import sys
import flightgear.meta.logging
import flightgear.meta.i18n as fg_i18n
PROGNAME = os.path.basename(sys.argv[0])
# Only messages with severity >= info will be printed to the terminal (it's
# possible to also log all messages to a file regardless of their level, see
# the Logger class). Of course, there is also the standard logging module...
logger = flightgear.meta.logging.Logger(
progname=PROGNAME,
logLevel=flightgear.meta.logging.LogLevel.info,
defaultOutputStream=sys.stderr)
debug = logger.debug
info = logger.info
notice = logger.notice
warning = logger.warning
error = logger.error
critical = logger.critical
# We could use Translation.__str__(): not as readable (for now) but more
# accurate on metadata
def printPlainText(l10nResPoolMgr, translations):
"""Print output suitable for a quick review (by the programmer)."""
firstLang = True
for langCode, (transl, nbWhitespacePbs) in translations.items():
# 'transl' is a Translation instance
if firstLang:
firstLang = False
else:
print()
print("-" * 78 + "\n" + langCode + "\n" + "-" * 78)
print("\nNumber of leading and/or trailing whitespace problems: {}"
.format(nbWhitespacePbs))
for cat in transl:
print("\nCategory: {cat}\n{underline}".format(
cat=cat, underline="~"*(len("Category: ") + len(cat))))
t = transl[cat]
for tid, translUnit in sorted(t.items()):
# - Using '{master!r}' and '{transl!r}' prints stuff such as
# \xa0 for nobreak spaces, which can lead to the erroneous
# conclusion that there was an encoding problem.
# - Only printing the first target text here (no plural forms)
print("\n{id}\n '{sourceText}'\n '{targetText}'"
.format(id=tid.id(), sourceText=translUnit.sourceText,
targetText=translUnit.targetTexts[0]))
def writeXliff(l10nResPoolMgr, translations):
formatHandler = fg_i18n.XliffFormatHandler()
for langCode, translData in translations.items():
translation = translData.transl # Translation instance
if params.output_dir is None:
# Use default locations for the written xliff files
l10nResPoolMgr.writeTranslation(formatHandler, translation)
else:
basename = "{}-{}.{}".format(
formatHandler.defaultFileStem(langCode),
langCode,
formatHandler.standardExtension)
filePath = os.path.join(params.output_dir, basename)
formatHandler.writeTranslation(translation, filePath)
def processCommandLine():
params = argparse.Namespace()
parser = argparse.ArgumentParser(
usage="""\
%(prog)s [OPTION ...] LANGUAGE_CODE...
Convert FlightGear's old XML translation files into other formats.""",
description="""\
Most notably, XLIFF format can be chosen for output. The script performs
a few automated checks on the input files too.""",
formatter_class=argparse.RawDescriptionHelpFormatter,
# I want --help but not -h (it might be useful for something else)
add_help=False)
parser.add_argument("-t", "--transl-dir",
help="""\
directory containing all translation subdirs (such as
{default!r}, 'en_GB', 'fr_FR', 'de', 'it'...). This
"option" MUST be specified.""".format(
default=fg_i18n.DEFAULT_LANG_DIR))
parser.add_argument("lang_code", metavar="LANGUAGE_CODE", nargs="+",
help="""\
codes of languages to read translations for (don't
specify {default!r} this way, it is special and not a
language code)"""
.format(default=fg_i18n.DEFAULT_LANG_DIR))
parser.add_argument("-o", "--output-dir",
help="""\
output directory for written XLIFF files
(default: for each output file, use a suitable location
under TRANSL_DIR)""")
parser.add_argument("-f", "--output-format", default="xliff",
choices=("xliff", "text"), help="""\
format to use for the output files""")
parser.add_argument("--help", action="help",
help="display this message and exit")
params = parser.parse_args(namespace=params)
if params.transl_dir is None:
error("--transl-dir must be given, aborting")
sys.exit(1)
return params
def main():
global params
locale.setlocale(locale.LC_ALL, '')
params = processCommandLine()
l10nResPoolMgr = fg_i18n.L10NResourcePoolManager(params.transl_dir, logger)
# English version of all translatable strings
masterTransl, nbWhitespaceProblemsInMaster = \
l10nResPoolMgr.readFgMasterTranslation()
translations = collections.OrderedDict()
# Sort elements of 'translations' according to language code (= the keys)
for langCode in sorted(params.lang_code):
translationData = l10nResPoolMgr.readFgTranslation(masterTransl,
langCode)
translations[translationData.transl.targetLanguage] = translationData
if params.output_format == "xliff":
writeFunc = writeXliff # write to files
elif params.output_format == "text":
writeFunc = printPlainText # print to stdout
else:
assert False, \
"Unexpected output format: '{}'".format(params.output_format)
writeFunc(l10nResPoolMgr, translations)
nbWhitespaceProblemsInTransl = sum(
(translData.nbWhitespacePbs for translData in translations.values() ))
info("total number of leading and/or trailing whitespace problems: {}"
.format(nbWhitespaceProblemsInMaster + nbWhitespaceProblemsInTransl))
sys.exit(0)
if __name__ == "__main__": main()

View File

@@ -1,123 +0,0 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# fg-merge-xliff-into-xliff --- Merge translations from one XLIFF file into
# another one
# Copyright (C) 2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import argparse
import locale
import os
import sys
import flightgear.meta.logging
import flightgear.meta.i18n as fg_i18n
PROGNAME = os.path.basename(sys.argv[0])
# Only messages with severity >= info will be printed to the terminal (it's
# possible to also log all messages to a file regardless of their level, see
# the Logger class). Of course, there is also the standard logging module...
logger = flightgear.meta.logging.Logger(
progname=PROGNAME,
logLevel=flightgear.meta.logging.LogLevel.info,
defaultOutputStream=sys.stderr)
def processCommandLine():
params = argparse.Namespace()
parser = argparse.ArgumentParser(
usage="""\
%(prog)s [OPTION ...] SOURCE INTO
Merge strings from a FlightGear XLIFF localization file into another one.""",
description="""\
This program merges a FlightGear XLIFF localization file into another one.
This means that every translatable string that:
(1) exists in both SOURCE and INTO;
(2) has the same target language, source text, plural status and number of
plural forms in SOURCE and in INTO;
is updated from SOURCE, i.e.: the target texts, 'approved' status and
translator comments are copied from SOURCE.
The result is written to INTO unless the -o (--output) option is given.
Note that this program is different from fg-update-translation-files's
'merge-new-master' command, which is for updating an XLIFF file according to
the default translation ("master").
Expected use case: suppose that a translator is working on a translation
file, and meanwhile the official XLIFF file for this translation is updated
in the project repository (new translatable strings added, obsolete strings
marked or removed, etc.). This program can then be used to merge the
translator work into the project file for all strings for which it makes
sense (source text unchanged, same plural status, etc.).""",
formatter_class=argparse.RawDescriptionHelpFormatter,
# I want --help but not -h (it might be useful for something else)
add_help=False)
parser.add_argument("source", metavar="SOURCE",
help="""\
input XLIFF file; read updated translated strings
from this file""")
parser.add_argument("into", metavar="INTO",
help="""\
XLIFF file to compare to SOURCE in order to decide
which translated strings to update; unless the -o
option is used, updated strings are written to this
file""")
parser.add_argument("-o", "--output",
help="""\
write the XLIFF merged output to OUTPUT instead of
INTO. When this option is used, INTO is read but not
modified. If OUTPUT is '-', write the XLIFF merged
output to the standard output.""")
parser.add_argument("--help", action="help",
help="display this message and exit")
return parser.parse_args(namespace=params)
def mergeXliffIntoXliff(source, into, output):
formatHandler = fg_i18n.XliffFormatHandler()
srcTransl = formatHandler.readTranslation(source)
transl = formatHandler.readTranslation(into)
# Merge 'srcTransl' into 'transl'
transl.mergeNonMasterTransl(srcTransl, logger=logger)
# File path, or '-' for the standard output
outputFile = into if output is None else output
formatHandler.writeTranslation(transl, outputFile)
def main():
global params
locale.setlocale(locale.LC_ALL, '')
params = processCommandLine()
mergeXliffIntoXliff(params.source, params.into, params.output)
sys.exit(0)
if __name__ == "__main__": main()

View File

@@ -1,120 +0,0 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# fg-new-translations --- Create new translations for FlightGear
# Copyright (C) 2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import argparse
import collections
import locale
import os
import sys
import flightgear.meta.logging
import flightgear.meta.i18n as fg_i18n
PROGNAME = os.path.basename(sys.argv[0])
# Only messages with severity >= info will be printed to the terminal (it's
# possible to also log all messages to a file regardless of their level, see
# the Logger class). Of course, there is also the standard logging module...
logger = flightgear.meta.logging.Logger(
progname=PROGNAME,
logLevel=flightgear.meta.logging.LogLevel.info,
defaultOutputStream=sys.stderr)
def processCommandLine():
params = argparse.Namespace()
parser = argparse.ArgumentParser(
usage="""\
%(prog)s [OPTION ...] LANGUAGE_CODE...
Write the skeleton of XLIFF translation files.""",
description="""\
This program writes XLIFF translation files with the strings to translate
for the specified languages (target strings are empty). This is what you need
to start a translation for a new language.""",
formatter_class=argparse.RawDescriptionHelpFormatter,
# I want --help but not -h (it might be useful for something else)
add_help=False)
parser.add_argument("-t", "--transl-dir",
help="""\
directory containing all translation subdirs (such as
{default!r}, 'en_GB', 'fr_FR', 'de', 'it'...). This
"option" MUST be specified.""".format(
default=fg_i18n.DEFAULT_LANG_DIR))
parser.add_argument("lang_code", metavar="LANGUAGE_CODE", nargs="+",
help="""\
codes of languages to create translations for (e.g., fr,
fr_BE, en_GB, it, es_ES...)""")
parser.add_argument("-o", "--output-file",
help="""\
where to write the output to (use '-' for standard
output); if not specified, a suitable file under
TRANSL_DIR will be chosen for each LANGUAGE_CODE.
Note: this option can only be given when exactly one
LANGUAGE_CODE has been specified on the command
line (it doesn't make sense otherwise).""")
parser.add_argument("--output-format", default="xliff",
choices=fg_i18n.FORMAT_HANDLERS_NAMES,
help="format to use for the output files")
parser.add_argument("--help", action="help",
help="display this message and exit")
params = parser.parse_args(namespace=params)
if params.transl_dir is None:
logger.error("--transl-dir must be given, aborting")
sys.exit(1)
if params.output_file is not None and len(params.lang_code) > 1:
logger.error("--output-file can only be given when exactly one "
"LANGUAGE_CODE has been specified on the command line "
"(it doesn't make sense otherwise)")
sys.exit(1)
return params
def main():
global params
locale.setlocale(locale.LC_ALL, '')
params = processCommandLine()
l10nResPoolMgr = fg_i18n.L10NResourcePoolManager(params.transl_dir, logger)
xliffFormatHandler = fg_i18n.FORMAT_HANDLERS_MAP[params.output_format]()
if params.output_file is not None:
assert len(params.lang_code) == 1, params.lang_code
# Output to one file or to stdout
l10nResPoolMgr.writeSkeletonTranslation(
xliffFormatHandler, params.lang_code[0],
filePath=params.output_file)
else:
# Output to several files
for langCode in params.lang_code:
l10nResPoolMgr.writeSkeletonTranslation(xliffFormatHandler,
langCode)
sys.exit(0)
if __name__ == "__main__": main()

View File

@@ -1,179 +0,0 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# fg-update-translation-files --- Merge new default translation,
# remove obsolete strings from a translation
# Copyright (C) 2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import argparse
import enum
import locale
import os
import sys
import flightgear.meta.logging
import flightgear.meta.i18n as fg_i18n
PROGNAME = os.path.basename(sys.argv[0])
# Only messages with severity >= info will be printed to the terminal (it's
# possible to also log all messages to a file regardless of their level, see
# the Logger class). Of course, there is also the standard logging module...
logger = flightgear.meta.logging.Logger(
progname=PROGNAME,
logLevel=flightgear.meta.logging.LogLevel.info,
defaultOutputStream=sys.stderr)
def processCommandLine():
params = argparse.Namespace()
parser = argparse.ArgumentParser(
usage="""\
%(prog)s [OPTION ...] ACTION LANGUAGE_CODE...
Update FlightGear XLIFF localization files.""",
description="""\
This program performs the following operations (actions) on FlightGear XLIFF
translation files (*.xlf):
- [merge-new-master]
Read the default translation[1], add new translated strings it contains to
the XLIFF localization files corresponding to the specified language(s),
mark the translated strings in said files that need review (modified in
the default translation) as well as those that are not used anymore
(disappeared in the default translation, or marked in a way that says they
don't need to be translated);
- [mark-unused]
Read the default translation and mark translated strings (in the XLIFF
localization files corresponding to the specified language(s)) that are
not used anymore;
- [remove-unused]
In the XLIFF localization files corresponding to the specified
language(s), remove all translated strings that are marked as unused.
A translated string that is marked as unused is still present in the XLIFF
localization file; it is just presented in a way that tells translators they
don't need to worry about it. On the other hand, when a translated string is
removed, translators don't see it anymore and the translation is lost, except
if rescued by external means such as backups or version control systems (Git,
Subversion, etc.)
Note that the 'remove-unused' action does *not* imply 'mark-unused'. It only
removes translation units that are already marked as unused (i.e., with
translate="no"). Thus, it makes sense to do 'mark-unused' followed by
'remove-unused' if you really want to get rid of old translations (you need to
invoke the program twice, or make a small change for this). Leaving unused
translated strings marked as such in XLIFF files shouldn't harm much in
general on the short or mid-term: they only take some space.
[1] FlightGear XML files in $FG_ROOT/Translations/default containing strings
used for the default locale (English).""",
formatter_class=argparse.RawDescriptionHelpFormatter,
# I want --help but not -h (it might be useful for something else)
add_help=False)
parser.add_argument("-t", "--transl-dir",
help="""\
directory containing all translation subdirs (such as
{default!r}, 'en_GB', 'fr_FR', 'de', 'it'...). This
"option" MUST be specified.""".format(
default=fg_i18n.DEFAULT_LANG_DIR))
parser.add_argument("action", metavar="ACTION",
choices=("merge-new-master",
"mark-unused",
"remove-unused"),
help="""\
what to do: merge a new default (= master)
translation, or mark unused translation units, or
remove those already marked as unused from the XLIFF
files corresponding to each given LANGUAGE_CODE (i.e.,
those that are not in the default translation)""")
parser.add_argument("lang_code", metavar="LANGUAGE_CODE", nargs="+",
help="""\
codes of languages to operate on (e.g., fr, en_GB, it,
es_ES...)""")
parser.add_argument("--help", action="help",
help="display this message and exit")
params = parser.parse_args(namespace=params)
if params.transl_dir is None:
logger.error("--transl-dir must be given, aborting")
sys.exit(1)
return params
class MarkOrRemoveUnusedAction(enum.Enum):
mark, remove = range(2)
def markOrRemoveUnused(l10nResPoolMgr, action):
formatHandler = fg_i18n.XliffFormatHandler()
masterTransl = l10nResPoolMgr.readFgMasterTranslation().transl
for langCode in params.lang_code:
xliffPath = formatHandler.defaultFilePath(params.transl_dir, langCode)
transl = formatHandler.readTranslation(xliffPath)
if action == MarkOrRemoveUnusedAction.mark:
transl.markObsoleteOrVanished(masterTransl, logger=logger)
elif action == MarkOrRemoveUnusedAction.remove:
transl.removeObsoleteOrVanished(logger=logger)
else:
assert False, "unexpected action: {!r}".format(action)
l10nResPoolMgr.writeTranslation(formatHandler, transl,
filePath=xliffPath)
def mergeNewMaster(l10nResPoolMgr):
formatHandler = fg_i18n.XliffFormatHandler()
masterTransl = l10nResPoolMgr.readFgMasterTranslation().transl
for langCode in params.lang_code:
xliffPath = formatHandler.defaultFilePath(params.transl_dir, langCode)
transl = formatHandler.readTranslation(xliffPath)
transl.mergeMasterTranslation(masterTransl, logger=logger)
l10nResPoolMgr.writeTranslation(formatHandler, transl,
filePath=xliffPath)
def main():
global params
locale.setlocale(locale.LC_ALL, '')
params = processCommandLine()
l10nResPoolMgr = fg_i18n.L10NResourcePoolManager(params.transl_dir, logger)
if params.action == "mark-unused":
markOrRemoveUnused(l10nResPoolMgr, MarkOrRemoveUnusedAction.mark)
elif params.action == "remove-unused":
markOrRemoveUnused(l10nResPoolMgr, MarkOrRemoveUnusedAction.remove)
elif params.action == "merge-new-master":
mergeNewMaster(l10nResPoolMgr)
else:
assert False, "Bug: unexpected action: {!r}".format(params.action)
sys.exit(0)
if __name__ == "__main__": main()

View File

@@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
# exceptions.py --- Simple, general-purpose subclass of Exception
#
# Copyright (C) 2015, 2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""Simple, general-purpose Exception subclass."""
class FGPyException(Exception):
def __init__(self, message=None, *, mayCapitalizeMsg=True):
"""Initialize an FGPyException instance.
Except in cases where 'message' starts with a proper noun or
something like that, its first character should be given in
lower case. Automated treatments of this exception may print the
message with its first character changed to upper case, unless
'mayCapitalizeMsg' is False. In other words, if the case of the
first character of 'message' must not be changed under any
circumstances, set 'mayCapitalizeMsg' to False.
"""
self.message = message
self.mayCapitalizeMsg = mayCapitalizeMsg
def __str__(self):
return self.completeMessage()
def __repr__(self):
return "{}.{}({!r})".format(__name__, type(self).__name__, self.message)
# Typically overridden by subclasses with a custom constructor
def detail(self):
return self.message
def completeMessage(self):
if self.message:
return "{shortDesc}: {detail}".format(
shortDesc=self.ExceptionShortDescription,
detail=self.detail())
else:
return self.ExceptionShortDescription
ExceptionShortDescription = "FlightGear Python generic exception"

File diff suppressed because it is too large Load Diff

View File

@@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
# logging.py --- Simple logging infrastructure (mostly taken from FFGo)
# Copyright (C) 2015, 2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import sys
from . import misc
class LogLevel(misc.OrderedEnum):
debug, info, notice, warning, error, critical = range(6)
# List containing the above log levels as strings in increasing priority order
allLogLevels = [member.name for member in LogLevel]
allLogLevels.sort(key=lambda n: LogLevel[n].value)
def _logFuncFactory(level):
def logFunc(self, *args, **kwargs):
self.log(LogLevel[level], True, *args, **kwargs)
def logFunc_noPrefix(self, *args, **kwargs):
self.log(LogLevel[level], False, *args, **kwargs)
return (logFunc, logFunc_noPrefix)
class Logger:
def __init__(self, progname=None, logLevel=LogLevel.notice,
defaultOutputStream=sys.stdout, logFile=None):
self.progname = progname
self.logLevel = logLevel
self.defaultOutputStream = defaultOutputStream
self.logFile = logFile
def setLogFile(self, *args, **kwargs):
self.logFile = open(*args, **kwargs)
def log(self, level, printLogLevel, *args, **kwargs):
if printLogLevel and level >= LogLevel.warning and args:
args = [level.name.upper() + ": " + args[0]] + list(args[1:])
if level >= self.logLevel:
if (self.progname is not None) and args:
tArgs = [self.progname + ": " + args[0]] + list(args[1:])
else:
tArgs = args
kwargs["file"] = self.defaultOutputStream
print(*tArgs, **kwargs)
if self.logFile is not None:
kwargs["file"] = self.logFile
print(*args, **kwargs)
# Don't overload log() with too many tests or too much indirection for
# little use
def logToFile(self, *args, **kwargs):
kwargs["file"] = self.logFile
print(*args, **kwargs)
# NP functions are “no prefix” variants which never prepend the log level
# (otherwise, it is only prepended for warning and higher levels).
debug, debugNP = _logFuncFactory("debug")
info, infoNP = _logFuncFactory("info")
notice, noticeNP = _logFuncFactory("notice")
warning, warningNP = _logFuncFactory("warning")
error, errorNP = _logFuncFactory("error")
critical, criticalNP = _logFuncFactory("critical")
class DummyLogger(Logger):
def setLogFile(self, *args, **kwargs):
pass
def log(self, *args, **kwargs):
pass
def logToFile(self, *args, **kwargs):
pass

View File

@@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
# misc.py --- Miscellaneous classes and/or functions
# Copyright (C) 2015-2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import enum
# Based on an example from the 'enum' documentation
class OrderedEnum(enum.Enum):
"""Base class for enumerations whose members can be ordered.
Contrary to enum.IntEnum, this class maintains normal enum.Enum
invariants, such as members not being comparable to members of other
enumerations (nor of any other class, actually).
"""
def __ge__(self, other):
if self.__class__ is other.__class__:
return self.value >= other.value
return NotImplemented
def __gt__(self, other):
if self.__class__ is other.__class__:
return self.value > other.value
return NotImplemented
def __le__(self, other):
if self.__class__ is other.__class__:
return self.value <= other.value
return NotImplemented
def __lt__(self, other):
if self.__class__ is other.__class__:
return self.value < other.value
return NotImplemented
def __eq__(self, other):
if self.__class__ is other.__class__:
return self.value == other.value
return NotImplemented
def __ne__(self, other):
if self.__class__ is other.__class__:
return self.value != other.value
return NotImplemented
# Taken from <http://effbot.org/zone/element-lib.htm#prettyprint> and modified
# by Florent Rougon
def indentXmlTree(elem, level=0, basicOffset=2, lastChild=False):
def indentation(level):
return "\n" + level*basicOffset*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = indentation(level+1)
for e in elem[:-1]:
indentXmlTree(e, level+1, basicOffset, False)
if len(elem):
indentXmlTree(elem[-1], level+1, basicOffset, True)
if level and (not elem.tail or not elem.tail.strip()):
if lastChild:
elem.tail = indentation(level-1)
else:
elem.tail = indentation(level)

View File

@@ -1,172 +0,0 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# rebuild-fgdata-embedded-resources -- Rebuild FGData-resources.[ch]xx
# Copyright (C) 2017 Florent Rougon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# Only standard modules so that distributors can easily run this script, in
# case they want to recreate FGData-resources.[ch]xx from source.
import argparse
import json
import locale
import logging
import os
import subprocess
import sys
PROGNAME = os.path.basename(sys.argv[0])
CONFIG_FILE = os.path.join(os.path.expanduser('~'),
".fgmeta",
PROGNAME + ".json")
# chLevel: console handler level
def setupLogging(level=logging.NOTSET, chLevel=None):
global logger
if chLevel is None:
chLevel = level
logger = logging.getLogger(__name__)
# Effective level for all child loggers with NOTSET level
logger.setLevel(level)
# Create console handler and set its level
ch = logging.StreamHandler() # Uses sys.stderr by default
ch.setLevel(chLevel) # NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL
# Logger name with :%(name)s... many other things available, including
# %(levelname)s
formatter = logging.Formatter("{}: %(message)s".format(PROGNAME))
# Add formatter to ch
ch.setFormatter(formatter)
# Add ch to logger
logger.addHandler(ch)
# Modifies 'params' in-place
def loadCfgFileSection(params, jsonTree, title, items):
# NB: !!! each item is subject to os.path.expanduser() !!!
try:
section = jsonTree[title]
except KeyError:
pass
else:
for name in items:
try:
path = section[name]
except KeyError:
pass
else:
setattr(params, name.lower(), os.path.expanduser(path))
# Modifies 'params' in-place
def loadConfigFile(params):
if not os.path.isfile(CONFIG_FILE):
return
# The log level is set too late for this one -> commented out
# logger.info("Loading config file {}...".format(CONFIG_FILE))
with open(CONFIG_FILE, "r", encoding="utf-8") as cfgFile:
tree = json.load(cfgFile)
loadCfgFileSection(params, tree, "repositories", ("FlightGear", "FGData"))
loadCfgFileSection(params, tree, "executables", ("fgrcc",))
def processCommandLine(params):
parser = argparse.ArgumentParser(
usage="""\
%(prog)s [OPTION ...]
Rebuild FGData embedded resources for FlightGear.""",
description="""\
Use fgrcc with FGData-resources.xml and the corresponding files in FGData to
(re)create the FGData-resources.[ch]xx files used in the FlightGear build. The
existing files in the FlightGear repository are always overwritten
(FGData-resources.[ch]xx in <FlightGear-repo>/src/EmbeddedResources).
This is a dumb script that simply calls fgrcc with appropriate parameters. In
order to save some typing, you may want to use a configuration file like this
(~/.fgmeta/%(prog)s.json):
{"repositories": {"FlightGear": "~/flightgear/src/flightgear",
"FGData": "~/flightgear/src/fgdata"},
"executables": {"fgrcc":
"~/flightgear/src/build-fg/src/EmbeddedResources/fgrcc"
}
}""",
formatter_class=argparse.RawDescriptionHelpFormatter,
# I want --help but not -h (it might be useful for something else)
add_help=False)
parser.add_argument('--flightgear', action='store', help="""\
Path to the FlightGear repository""")
parser.add_argument('--fgdata', action='store', help="""\
Path to the FGData repository""")
parser.add_argument('--fgrcc', action='store', help="""\
Path to the fgrcc executable""")
parser.add_argument('--log-level', action='store',
choices=("debug", "info", "warning", "error",
"critical"),
default=None, help="""Set the log level""")
parser.add_argument('--help', action="help",
help="display this message and exit")
parser.parse_args(namespace=params)
# Don't use the 'default' argparse mechanism for this, in order to allow
# the config file to set the log level in a meaningful way if we want (not
# implemented at the time of this writing).
if params.log_level is not None:
logger.setLevel(getattr(sys.modules["logging"],
params.log_level.upper()))
def main():
locale.setlocale(locale.LC_ALL, '')
setupLogging(level=logging.INFO) # may be overridden by options
params = argparse.Namespace()
loadConfigFile(params) # could set the log level
processCommandLine(params)
if (params.flightgear is None or params.fgdata is None or
params.fgrcc is None):
logger.error(
"--flightgear, --fgdata and --fgrcc must all be specified (they "
"may be set in the config file; use --help for more info)")
sys.exit(1)
resDir = os.path.join(params.flightgear, "src", "EmbeddedResources")
inputXMLFile = os.path.join(resDir, "FGData-resources.xml")
cxxFile = os.path.join(resDir, "FGData-resources.cxx")
hxxFile = os.path.join(resDir, "FGData-resources.hxx")
args = [params.fgrcc,
"--root={}".format(params.fgdata),
"--output-cpp-file={}".format(cxxFile),
"--init-func-name=initFGDataEmbeddedResources",
"--output-header-file={}".format(hxxFile),
"--output-header-identifier=_FG_FGDATA_EMBEDDED_RESOURCES",
inputXMLFile]
# encoding="utf-8" requires Python >= 3.6 -> will add it later
# (it's not really needed, as we don't process the output)
subprocess.run(args, check=True)
sys.exit(0)
if __name__ == "__main__": main()

View File

@@ -12,7 +12,7 @@
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with FlightGear If not, see <http://www.gnu.org/licenses/>.
#along with Foobar. If not, see <http://www.gnu.org/licenses/>.
if [ -z "$1" -o -z "$2" ]; then
echo "usage: thismajor.thisminor nextmajor.nextminor path"
@@ -65,6 +65,8 @@ createBranch() {
while [ $# -gt 0 ]; do
echo "Processing $1"
pushd $1 > /dev/null
git config user.name "Automatic Release Builder"
git config user.email "build@flightgear.org"
createBranch
popd > /dev/null
shift

View File

@@ -1,8 +1,8 @@
#!/bin/bash
THIS_RELEASE="2018.2"
NEXT_RELEASE="2018.3"
SUBMODULES="simgear flightgear fgdata getstart"
THIS_RELEASE="2017.1"
NEXT_RELEASE="2017.2"
SUBMODULES="simgear flightgear fgdata"
#:<< 'COMMENT_END'
git checkout next
@@ -28,7 +28,7 @@ read something
for f in $SUBMODULES .; do
pushd "$f"
echo "Pushing $f"
git checkout release/${THIS_RELEASE} && git push origin release/${THIS_RELEASE} && git push origin version/${THIS_RELEASE}.1 && git push origin version/${NEXT_RELEASE}.0 && git checkout next && git push
git checkout release/${THIS_RELEASE} && git push origin release/${THIS_RELEASE} && git push origin version/${THIS_RELEASE}.1 && git push origin version/${NEXT_RELEASE}.0 && git checkout next && git push
popd
done
@@ -42,3 +42,4 @@ done
svn copy svn+ssh://svn.code.sf.net/p/flightgear/fgaddon/trunk \
svn+ssh://svn.code.sf.net/p/flightgear/fgaddon/branches/release-${THIS_RELEASE} \
-m "branching for release ${THIS_RELEASE}"

Submodule simgear updated: 489573329e...e084bd89e7

View File

@@ -1 +1 @@
2018.2.1
2017.1.2