Switching to python build script.

This commit is contained in:
Ben Vanik 2015-07-31 23:48:24 -07:00
parent 2f1965082c
commit b56b262116
8 changed files with 1006 additions and 799 deletions

22
.travis.yml Normal file
View File

@ -0,0 +1,22 @@
# Make Travis use docker (for faster builds, in theory)
sudo: false
language: cpp
compiler:
- clang
# - gcc
os:
- linux
# - osx
# Run setup to build ninja/gyp/etc and actually build xenia.
before_script:
- travis_retry ./xenia-build setup
- ./xenia-build build --config=debug
# Run test suites.
script:
- ./xenia-build lint --all
- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=false
- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=true

View File

@ -45,7 +45,7 @@ Windows 8.1+ with Python 2.7 and [Visual Studio 2015](https://www.visualstudio.c
# Build on command line:
> xb build
# Run premake and open Visual Studio (run the 'xenia-app' project):
> xb edit
> xb devenv
# Run premake to update the sln/vcproj's:
> xb premake

@ -1 +1 @@
Subproject commit ff0bab3c8e3579684b0a5634d97f181bca8d0543
Subproject commit 13552a8942c1fa5527e80698516912ad08d9e1ab

View File

@ -46,12 +46,18 @@ void AttachConsole() {
setvbuf(stderr, nullptr, _IONBF, 0);
}
} // namespace xe
// Used in console mode apps; automatically picked based on subsystem.
int main(int argc, wchar_t* argv[]) {
int Main() {
auto entry_info = xe::GetEntryInfo();
// Convert command line to an argv-like format so we can share code/use
// gflags.
auto command_line = GetCommandLineW();
int argc;
wchar_t** argv = CommandLineToArgvW(command_line, &argc);
if (!argv) {
return 1;
}
google::SetUsageMessage(std::string("usage: ") +
xe::to_string(entry_info.usage));
google::SetVersionString("1.0");
@ -82,30 +88,23 @@ int main(int argc, wchar_t* argv[]) {
int result = entry_info.entry_point(args);
google::ShutDownCommandLineFlags();
LocalFree(argv);
return result;
}
} // namespace xe
// Used in console mode apps; automatically picked based on subsystem.
int main(int argc_ignored, char** argv_ignored) { return xe::Main(); }
// Used in windowed apps; automatically picked based on subsystem.
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR command_line, int) {
// Attach a console so we can write output to stdout. If the user hasn't
// redirected output themselves it'll pop up a window.
xe::AttachConsole();
auto entry_info = xe::GetEntryInfo();
// Convert to an argv-like format so we can share code/use gflags.
std::wstring buffer = entry_info.name + L" " + command_line;
int argc;
wchar_t** argv = CommandLineToArgvW(buffer.c_str(), &argc);
if (!argv) {
return 1;
}
// Run normal entry point.
int result = main(argc, argv);
LocalFree(argv);
return result;
return xe::Main();
}
#if defined _M_IX86

View File

@ -62,6 +62,57 @@ LPSYMGETSYMFROMADDR64 sym_get_sym_from_addr_64_ = nullptr;
namespace xe {
namespace cpu {
bool InitializeStackWalker() {
if (sym_get_options_) {
// Already initialized.
return true;
}
// Attempt to load dbghelp.
// NOTE: we never free it. That's fine.
HMODULE module = LoadLibrary(TEXT("dbghelp.dll"));
if (!module) {
XELOGE("Unable to load dbghelp.dll - not found on path or invalid");
return false;
}
sym_get_options_ = reinterpret_cast<LPSYMGETOPTIONS>(
GetProcAddress(module, "SymGetOptions"));
sym_set_options_ = reinterpret_cast<LPSYMSETOPTIONS>(
GetProcAddress(module, "SymSetOptions"));
sym_initialize_ = reinterpret_cast<LPSYMINITIALIZE>(
GetProcAddress(module, "SymInitialize"));
stack_walk_64_ =
reinterpret_cast<LPSTACKWALK64>(GetProcAddress(module, "StackWalk64"));
sym_function_table_access_64_ = reinterpret_cast<LPSYMFUNCTIONTABLEACCESS64>(
GetProcAddress(module, "SymFunctionTableAccess64"));
sym_get_module_base_64_ = reinterpret_cast<LPSYMGETMODULEBASE64>(
GetProcAddress(module, "SymGetModuleBase64"));
sym_get_sym_from_addr_64_ = reinterpret_cast<LPSYMGETSYMFROMADDR64>(
GetProcAddress(module, "SymGetSymFromAddr64"));
if (!sym_get_options_ || !sym_set_options_ || !sym_initialize_ ||
!stack_walk_64_ || !sym_function_table_access_64_ ||
!sym_get_module_base_64_ || !sym_get_sym_from_addr_64_) {
XELOGE("Unable to get one or more symbols from dbghelp.dll");
return false;
}
// Initialize the symbol lookup services.
DWORD options = sym_get_options_();
if (FLAGS_debug_symbol_loader) {
options |= SYMOPT_DEBUG;
}
options |= SYMOPT_DEFERRED_LOADS;
options |= SYMOPT_LOAD_LINES;
options |= SYMOPT_FAIL_CRITICAL_ERRORS;
sym_set_options_(options);
if (!sym_initialize_(GetCurrentProcess(), nullptr, TRUE)) {
XELOGE("Unable to initialize symbol services");
return false;
}
return true;
}
class Win32StackWalker : public StackWalker {
public:
Win32StackWalker(backend::CodeCache* code_cache) {
@ -77,51 +128,7 @@ class Win32StackWalker : public StackWalker {
bool Initialize() {
std::lock_guard<std::mutex> lock(dbghelp_mutex_);
// Attempt to load dbghelp.
// NOTE: we never free it. That's fine.
HMODULE module = LoadLibrary(TEXT("dbghelp.dll"));
if (!module) {
XELOGE("Unable to load dbghelp.dll - not found on path or invalid");
return false;
}
sym_get_options_ = reinterpret_cast<LPSYMGETOPTIONS>(
GetProcAddress(module, "SymGetOptions"));
sym_set_options_ = reinterpret_cast<LPSYMSETOPTIONS>(
GetProcAddress(module, "SymSetOptions"));
sym_initialize_ = reinterpret_cast<LPSYMINITIALIZE>(
GetProcAddress(module, "SymInitialize"));
stack_walk_64_ =
reinterpret_cast<LPSTACKWALK64>(GetProcAddress(module, "StackWalk64"));
sym_function_table_access_64_ =
reinterpret_cast<LPSYMFUNCTIONTABLEACCESS64>(
GetProcAddress(module, "SymFunctionTableAccess64"));
sym_get_module_base_64_ = reinterpret_cast<LPSYMGETMODULEBASE64>(
GetProcAddress(module, "SymGetModuleBase64"));
sym_get_sym_from_addr_64_ = reinterpret_cast<LPSYMGETSYMFROMADDR64>(
GetProcAddress(module, "SymGetSymFromAddr64"));
if (!sym_get_options_ || !sym_set_options_ || !sym_initialize_ ||
!stack_walk_64_ || !sym_function_table_access_64_ ||
!sym_get_module_base_64_ || !sym_get_sym_from_addr_64_) {
XELOGE("Unable to get one or more symbols from dbghelp.dll");
return false;
}
// Initialize the symbol lookup services.
DWORD options = sym_get_options_();
if (FLAGS_debug_symbol_loader) {
options |= SYMOPT_DEBUG;
}
options |= SYMOPT_DEFERRED_LOADS;
options |= SYMOPT_LOAD_LINES;
options |= SYMOPT_FAIL_CRITICAL_ERRORS;
sym_set_options_(options);
if (!sym_initialize_(GetCurrentProcess(), nullptr, TRUE)) {
XELOGE("Unable to initialize symbol services");
return false;
}
return true;
return InitializeStackWalker();
}
size_t CaptureStackTrace(uint64_t* frame_host_pcs, size_t frame_offset,

737
xb.bat
View File

@ -3,22 +3,9 @@ REM Copyright 2015 Ben Vanik. All Rights Reserved.
SET DIR=%~dp0
SET XENIA_SLN=build\xenia.sln
SET VS14_VCVARSALL="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
SET VS15_VCVARSALL="C:\Program Files (x86)\Microsoft Visual Studio 15.0\VC\vcvarsall.bat"
REM ============================================================================
REM Environment Validation
REM ============================================================================
REM To make life easier we just require everything before we try running.
CALL :check_git
IF %_RESULT% NEQ 0 (
ECHO ERROR:
ECHO git must be installed and on PATH.
GOTO :exit_error
)
CALL :check_python
IF %_RESULT% NEQ 0 (
@ -28,672 +15,13 @@ IF %_RESULT% NEQ 0 (
GOTO :exit_error
)
CALL :check_msvc
IF %_RESULT% NEQ 0 (
ECHO ERROR:
ECHO Visual Studio 2015 must be installed.
ECHO.
ECHO The Community Edition is free and can be downloaded here:
ECHO https://www.visualstudio.com/downloads/visual-studio-2015-downloads-vs
ECHO.
ECHO Once installed, launch the 'Developer Command Prompt for VS2015' and run
ECHO this script again.
GOTO :exit_error
)
1>NUL 2>NUL CMD /c where devenv
IF %ERRORLEVEL% NEQ 0 (
CALL %VS14_VCVARSALL% amd64
)
SET CLANG_FORMAT=""
SET LLVM_CLANG_FORMAT="C:\Program Files (x86)\LLVM\bin\clang-format.exe"
IF EXIST %LLVM_CLANG_FORMAT% (
SET CLANG_FORMAT=%LLVM_CLANG_FORMAT%
) ELSE (
1>NUL 2>NUL CMD /c where clang-format
IF %ERRORLEVEL% NEQ 0 (
SET CLANG_FORMAT="clang-format"
)
)
REM ============================================================================
REM Command Parsing
REM Trampoline into xenia-build
REM ============================================================================
SET _RESULT=-2
REM Ensure a command has been passed.
IF -%1-==-- GOTO :show_help
REM Dispatch to handler (:perform_foo).
2>NUL CALL :perform_%1 %*
IF %_RESULT% EQU -2 GOTO :show_help
IF %_RESULT% EQU -1 GOTO :exit_nop
IF %_RESULT% EQU 0 GOTO :exit_success
GOTO :exit_error
:exit_success
ECHO.
ECHO OK
EXIT /b 0
:exit_error
ECHO.
ECHO Error: %_RESULT%
EXIT /b 1
:exit_nop
ECHO.
ECHO (no actions performed)
EXIT /b 0
REM ============================================================================
REM xb help
REM ============================================================================
:show_help
SETLOCAL
ECHO.
ECHO Usage: xb COMMAND [options]
ECHO.
ECHO Commands:
ECHO.
ECHO xb setup
ECHO Initializes dependencies and prepares build environment.
ECHO.
ECHO xb pull [--rebase]
ECHO Fetches latest changes from github and rebuilds dependencies.
ECHO.
ECHO xb premake
ECHO Regenerates Visual Studio projects and makefiles.
ECHO.
ECHO xb proto
ECHO Regenerates protocol files (*.fbs).
ECHO.
ECHO xb edit
ECHO Opens Visual Studio with `xenia.sln`.
ECHO.
ECHO xb build [--checked OR --debug OR --release] [--force]
ECHO Initializes dependencies and prepares build environment.
ECHO.
ECHO xb gentests
ECHO Generates test binaries (under src/xenia/cpu/frontend/testing/bin/).
ECHO Run after modifying test .s files.
ECHO.
ECHO xb test [--checked OR --debug OR --release] [--continue]
ECHO Runs automated tests. Tests must have been built with `xb build`.
ECHO.
ECHO xb clean
ECHO Cleans normal build artifacts to force a rebuild.
ECHO.
ECHO xb nuke
ECHO Resets branch and build environment to defaults to unhose state.
ECHO.
ECHO xb lint [--all] [--origin]
ECHO Runs linter on local changes (or entire codebase).
ECHO.
ECHO xb format [--all] [--origin]
ECHO Runs linter/auto-formatter on local changes (or entire codebase).
ECHO.
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb setup
REM ============================================================================
:perform_setup
SETLOCAL
ECHO Setting up the build environment...
ECHO.
ECHO ^> git submodule update --init --recursive
git submodule update --init --recursive
IF %ERRORLEVEL% NEQ 0 (
ECHO.
ECHO ERROR: failed to initialize git submodules
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ECHO.
ECHO ^> build_tools/premake ...
CALL :run_premake
IF %_RESULT% NEQ 0 (
ECHO.
ECHO ERROR: failed to run premake
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb pull
REM ============================================================================
:perform_pull
SETLOCAL
SET REBASE=0
SHIFT
:perform_pull_args
IF "%~1"=="" GOTO :perform_pull_parsed
IF "%~1"=="--" GOTO :perform_pull_parsed
IF "%~1"=="--rebase" (SET REBASE=1)
SHIFT
GOTO :perform_pull_args
:perform_pull_parsed
ECHO Pulling latest changes and rebuilding dependencies...
ECHO.
ECHO ^> git checkout master
git checkout master
IF %ERRORLEVEL% NEQ 0 (
ECHO.
ECHO ERROR: failed to checkout master
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ECHO.
IF %REBASE% EQU 1 (
ECHO ^> git pull --rebase
git pull --rebase
) ELSE (
ECHO ^> git pull
git pull
)
IF %ERRORLEVEL% NEQ 0 (
ECHO.
ECHO ERROR: failed to pull latest changes from git
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ECHO.
ECHO ^> git submodule update --recursive
git submodule update --recursive
IF %ERRORLEVEL% NEQ 0 (
ECHO.
ECHO ERROR: failed to update git submodules
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ECHO.
ECHO ^> build_tools/premake ...
CALL :run_premake
IF %_RESULT% NEQ 0 (
ECHO.
ECHO ERROR: failed to run premake
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb premake
REM ============================================================================
:perform_premake
SETLOCAL EnableDelayedExpansion
ECHO Generating project files...
ECHO.
ECHO ^> build_tools/premake ...
CALL :run_premake
IF %_RESULT% NEQ 0 (
ECHO.
ECHO ERROR: failed to run premake
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb proto
REM ============================================================================
:perform_proto
SETLOCAL EnableDelayedExpansion
ECHO Generating proto files...
SET FLATC=build\bin\Windows\Debug\flatc.exe
IF NOT EXIST %FLATC% (
SET FLATC=build\bin\Windows\Release\flatc.exe
IF NOT EXIST %FLATC% (
ECHO.
ECHO ERROR: flatc not built - build before running this
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
)
SET FBS_SRCS=src\xenia\debug\proto\
SET CC_OUT=src\xenia\debug\proto\
SET CS_OUT=src\Xenia.Debug\Proto\
SET ANY_ERRORS=0
PUSHD %FBS_SRCS%
FOR %%G in (*.fbs) DO (
ECHO ^> generating %%~nG...
POPD
SET SRC_FILE=%FBS_SRCS%\%%G
CMD /c build\bin\Windows\Debug\flatc.exe -c -o %CC_OUT% !SRC_FILE! 2>&1
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
CMD /c build\bin\Windows\Debug\flatc.exe -n -o %CS_OUT% !SRC_FILE! 2>&1
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
PUSHD %TEST_SRC%
)
POPD
IF %ANY_ERRORS% NEQ 0 (
ECHO.
ECHO ERROR: failed to build one or more tests
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb edit
REM ============================================================================
:perform_edit
SETLOCAL
ECHO Launching Visual Studio...
ECHO.
ECHO ^> build_tools/premake ...
CALL :run_premake
IF %_RESULT% NEQ 0 (
ECHO.
ECHO ERROR: failed to run premake
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ECHO.
ECHO ^> devenv %XENIA_SLN%
START devenv %XENIA_SLN%
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb build
REM ============================================================================
:perform_build
SETLOCAL
SET CONFIG="debug"
SET FORCE=0
SHIFT
:perform_build_args
IF "%~1"=="" GOTO :perform_build_parsed
IF "%~1"=="--" GOTO :perform_build_parsed
IF "%~1"=="--checked" (SET CONFIG="checked")
IF "%~1"=="--debug" (SET CONFIG="debug")
IF "%~1"=="--release" (SET CONFIG="release")
IF "%~1"=="--force" (SET FORCE=1)
SHIFT
GOTO :perform_build_args
:perform_build_parsed
ECHO Building for config %CONFIG%...
ECHO.
ECHO ^> build_tools/premake ...
CALL :run_premake
IF %_RESULT% NEQ 0 (
ECHO.
ECHO ERROR: failed to run premake
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
IF %FORCE% EQU 1 (
SET DEVENV_COMMAND=/rebuild
) ELSE (
SET DEVENV_COMMAND=/build
)
ECHO.
ECHO ^> devenv %XENIA_SLN% %DEVENV_COMMAND% %CONFIG%
1>NUL 2>NUL CMD /c where devenv
IF %ERRORLEVEL% NEQ 0 (
ECHO devenv not found
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
CMD /C devenv %XENIA_SLN% /nologo %DEVENV_COMMAND% %CONFIG%
IF %ERRORLEVEL% NEQ 0 (
ECHO.
ECHO ERROR: build failed with errors
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb gentests
REM ============================================================================
:perform_gentests
SETLOCAL EnableDelayedExpansion
ECHO Generating test binaries...
SET BINUTILS=third_party\binutils-ppc-cygwin
SET PPC_AS=%BINUTILS%\powerpc-none-elf-as.exe
SET PPC_LD=%BINUTILS%\powerpc-none-elf-ld.exe
SET PPC_OBJDUMP=%BINUTILS%\powerpc-none-elf-objdump.exe
SET PPC_NM=%BINUTILS%\powerpc-none-elf-nm.exe
SET TEST_SRC=src/xenia/cpu/frontend/testing
SET TEST_SRC_WIN=src\xenia\cpu\frontend\testing
SET TEST_BIN=%TEST_SRC%/bin
SET TEST_BIN_WIN=%TEST_SRC_WIN%\bin
IF NOT EXIST %TEST_BIN_WIN% (mkdir %TEST_BIN_WIN%)
SET ANY_ERRORS=0
PUSHD %TEST_SRC_WIN%
FOR %%G in (*.s) DO (
ECHO ^> generating %%~nG...
POPD
SET SRC_FILE=%TEST_SRC%/%%G
SET SRC_NAME=%%~nG
SET OBJ_FILE=%TEST_BIN%/!SRC_NAME!.o
CMD /c %PPC_AS% -a32 -be -mregnames -mpower7 -maltivec -mvsx -mvmx128 -R -o !OBJ_FILE! !SRC_FILE! 2>&1
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
%PPC_OBJDUMP% --adjust-vma=0x100000 -Mpower7 -Mvmx128 -D -EB !OBJ_FILE! > %TEST_BIN%/!SRC_NAME!.dis.tmp
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
REM Eat the first 4 lines to kill the file path that'll differ across machines.
MORE +4 %TEST_BIN_WIN%\!SRC_NAME!.dis.tmp > %TEST_BIN_WIN%\!SRC_NAME!.dis
DEL %TEST_BIN_WIN%\!SRC_NAME!.dis.tmp
CMD /c %PPC_LD% -A powerpc:common32 -melf32ppc -EB -nostdlib --oformat binary -Ttext 0x80000000 -e 0x80000000 -o %TEST_BIN%/!SRC_NAME!.bin !OBJ_FILE! 2>&1
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
%PPC_NM% --numeric-sort !OBJ_FILE! > %TEST_BIN%/!SRC_NAME!.map
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
PUSHD %TEST_SRC_WIN%
)
POPD
IF %ANY_ERRORS% NEQ 0 (
ECHO.
ECHO ERROR: failed to build one or more tests
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb test
REM ============================================================================
:perform_test
SETLOCAL EnableDelayedExpansion
SET BUILD=1
SET CONFIG="debug"
SET CONTINUE=0
SHIFT
:perform_test_args
IF "%~1"=="" GOTO :perform_test_parsed
IF "%~1"=="--" (
SHIFT
GOTO :perform_test_parsed
)
IF "%~1"=="--checked" (SET CONFIG="checked")
IF "%~1"=="--debug" (SET CONFIG="debug")
IF "%~1"=="--release" (SET CONFIG="release")
IF "%~1"=="--continue" (SET CONTINUE=1)
SHIFT
GOTO :perform_test_args
:perform_test_parsed
ECHO Running automated testing for config %CONFIG%...
SET TEST_NAMES=xenia-cpu-frontend-tests
FOR %%G IN (%TEST_NAMES%) DO (
IF NOT EXIST build\bin\Windows\%CONFIG%\%%G.exe (
ECHO.
ECHO ERROR: unable to find `%%G.exe` - ensure it is built.
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
)
SET ANY_FAILED=0
FOR %%G IN (%TEST_NAMES%) DO (
ECHO.
ECHO ^> build\bin\Windows\%CONFIG%\%%G.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
build\bin\Windows\%CONFIG%\%%G.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
IF !ERRORLEVEL! NEQ 0 (
SET ANY_FAILED=1
IF %CONTINUE% EQU 0 (
ECHO.
ECHO ERROR: test failed, aborting, use --continue to keep going
ENDLOCAL & SET _RESULT=1
GOTO :eof
) ELSE (
ECHO.
ECHO ERROR: test failed but continuing due to --continue
)
)
)
IF %ANY_FAILED% NEQ 0 (
ECHO.
ECHO ERROR: one or more tests failed
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb clean
REM ============================================================================
:perform_clean
SETLOCAL
ECHO Cleaning normal build outputs...
ECHO (use nuke to kill all artifacts)
1>NUL 2>NUL CMD /c where devenv
IF %ERRORLEVEL% NEQ 0 (
ECHO devenv not found
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
SET CONFIG_NAMES=Checked Debug Release
FOR %%G IN (%CONFIG_NAMES%) DO (
ECHO.
ECHO ^> devenv %XENIA_SLN% /clean %%G
CMD /C devenv %XENIA_SLN% /nologo /clean %%G
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb nuke
REM ============================================================================
:perform_nuke
SETLOCAL
ECHO Nuking all local changes...
ECHO.
ECHO.
ECHO ^> rmdir /s build
rmdir /s build
ECHO.
ECHO ^> git checkout --hard master
git checkout --hard master
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb lint
REM ============================================================================
:perform_lint
SETLOCAL EnableDelayedExpansion
SET ALL=0
SET HEAD=HEAD
SHIFT
:perform_lint_args
IF "%~1"=="" GOTO :perform_lint_parsed
IF "%~1"=="--" GOTO :perform_lint_parsed
IF "%~1"=="--all" (SET ALL=1)
IF "%~1"=="--origin" (SET HEAD=origin/master)
SHIFT
GOTO :perform_lint_args
:perform_lint_parsed
IF %ALL% EQU 1 (
ECHO Running code linter on all code...
) ELSE (
ECHO Running code linter on code staged in git index...
)
IF %CLANG_FORMAT%=="" (
ECHO.
ECHO ERROR: clang-format is not on PATH or the standard location.
ECHO LLVM is available from http://llvm.org/releases/download.html
ECHO See docs/style_guide.md for instructions on how to get it.
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
SET ANY_ERRORS=0
IF %ALL% NEQ 1 (
ECHO.
ECHO ^> git-clang-format
CMD /c python third_party/clang-format/git-clang-format --binary=%CLANG_FORMAT% --commit=%HEAD% --diff >.difftemp.txt
FIND /C "did not modify any files" .difftemp.txt >nul
IF !ERRORLEVEL! EQU 1 (
SET ANY_ERRORS=1
CMD /c python third_party/clang-format/git-clang-format --binary=%CLANG_FORMAT% --commit=%HEAD% --diff
)
DEL .difftemp.txt
) ELSE (
PUSHD src
FOR /R %%G in (*.cc *.c *.h *.inl) DO (
%CLANG_FORMAT% -output-replacements-xml -style=file %%G >.difftemp.txt
FIND /C "<replacement " .difftemp.txt >nul
IF !ERRORLEVEL! EQU 0 (
SET ANY_ERRORS=1
ECHO ^> clang-format %%G
DEL .difftemp.txt
%CLANG_FORMAT% -style=file %%G > .difftemp.txt
python ..\tools\diff.py "%%G" .difftemp.txt .difftemp.txt
TYPE .difftemp.txt
DEL .difftemp.txt
)
DEL .difftemp.txt
)
POPD
)
IF %ANY_ERRORS% NEQ 0 (
ECHO.
ECHO ERROR: 1+ diffs. Stage changes and run 'xb format' to fix.
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
REM ============================================================================
REM xb format
REM ============================================================================
:perform_format
SETLOCAL EnableDelayedExpansion
SET ALL=0
SET HEAD=HEAD
SHIFT
:perform_format_args
IF "%~1"=="" GOTO :perform_format_parsed
IF "%~1"=="--" GOTO :perform_format_parsed
IF "%~1"=="--all" (SET ALL=1)
IF "%~1"=="--origin" (SET HEAD=origin/master)
SHIFT
GOTO :perform_format_args
:perform_format_parsed
IF %ALL% EQU 1 (
ECHO Running code formatter on all code...
) ELSE (
ECHO Running code formatter on code staged in git index...
)
IF %CLANG_FORMAT%=="" (
ECHO.
ECHO ERROR: clang-format is not on PATH or the standard location.
ECHO LLVM is available from http://llvm.org/releases/download.html
ECHO See docs/style_guide.md for instructions on how to get it.
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
SET ANY_ERRORS=0
IF %ALL% NEQ 1 (
ECHO.
ECHO ^> git-clang-format
CMD /c python third_party/clang-format/git-clang-format --binary=%CLANG_FORMAT% --commit=%HEAD%
IF %ERRORLEVEL% NEQ 0 (
SET ANY_ERRORS=1
)
) ELSE (
PUSHD src
FOR /R %%G in (*.cc *.c *.h *.inl) DO (
ECHO ^> clang-format %%G
CMD /C %CLANG_FORMAT% -i -style=file %%G
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
)
POPD
PUSHD third_party\elemental-forms\src
FOR /R %%G in (*.cc *.c *.h *.inl) DO (
ECHO ^> clang-format %%G
CMD /C %CLANG_FORMAT% -i -style=file %%G
IF !ERRORLEVEL! NEQ 0 (
SET ANY_ERRORS=1
)
)
POPD
)
IF %ANY_ERRORS% NEQ 0 (
ECHO.
ECHO ERROR: 1+ clang-format calls failed - ensure all files are staged
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
python xenia-build %*
EXIT /b %ERRORLEVEL%
REM ============================================================================
@ -714,62 +42,3 @@ IF %ERRORLEVEL% NEQ 0 (
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
:check_git
1>NUL 2>NUL CMD /c where git
SET _RESULT=%ERRORLEVEL%
GOTO :eof
:check_msvc
SETLOCAL EnableDelayedExpansion
1>NUL 2>NUL CMD /c where devenv
IF %ERRORLEVEL% NEQ 0 (
IF EXIST %VS15_VCVARSALL% (
REM VS2015
ECHO Sourcing Visual Studio settings from %VS15_VCVARSALL%...
CALL %VS15_VCVARSALL% amd64
) ELSE (
IF EXIST %VS14_VCVARSALL% (
REM VS2015 CTP/RC
ECHO Sourcing Visual Studio settings from %VS14_VCVARSALL%...
CALL %VS14_VCVARSALL% amd64
)
)
)
1>NUL 2>NUL CMD /c where devenv
IF %ERRORLEVEL% NEQ 0 (
REM Still no devenv!
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
SET HAVE_TOOLS=0
IF "%VS140COMNTOOLS%" NEQ "" (
IF EXIST "%VS140COMNTOOLS%" (
REM VS2015 CTP/RC
SET HAVE_TOOLS=1
)
)
IF "%VS150COMNTOOLS%" NEQ "" (
IF EXIST "%VS150COMNTOOLS%" (
REM VS2015
SET HAVE_TOOLS=1
)
)
IF %HAVE_TOOLS% NEQ 1 (
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof
:run_premake
SETLOCAL EnableDelayedExpansion
CALL build_tools\premake.bat --file=premake5.lua --os=windows --test-suite-mode=combined --verbose vs2015
IF %ERRORLEVEL% NEQ 0 (
ECHO.
ECHO ERROR: failed to run premake
ENDLOCAL & SET _RESULT=1
GOTO :eof
)
ENDLOCAL & SET _RESULT=0
GOTO :eof

905
xenia-build Normal file
View File

@ -0,0 +1,905 @@
#!/usr/bin/env python
# Copyright 2015 Ben Vanik. All Rights Reserved.
"""Main build script and tooling for xenia.
Run with --help or no arguments for possible commands.
"""
__author__ = 'ben.vanik@gmail.com (Ben Vanik)'
import argparse
import os
import re
import shutil
import string
import subprocess
import sys
self_path = os.path.dirname(os.path.abspath(__file__))
def main():
# Add self to the root search path.
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
# Augment path to include our fancy things.
os.environ['PATH'] += os.pathsep + os.pathsep.join([
self_path,
os.path.abspath('build_tools/'),
])
# Check git exists.
if not has_bin('git'):
print('ERROR: git must be installed and on PATH.')
sys.exit(1)
return
# Check python version.
if not sys.version_info[:2] == (2, 7):
# TODO(benvanik): allow things to work with 3, but warn on clang-format.
print('ERROR: Python 2.7 must be installed and on PATH')
sys.exit(1)
return
# Grab Visual Studio version and execute shell to set up environment.
if sys.platform == 'win32':
vs_version = import_vs_environment()
if vs_version != 2015:
print('ERROR: Visual Studio 2015 not found!')
print('Ensure you have the VS140COMNTOOLS environment variable!')
sys.exit(1)
return
# Setup main argument parser and common arguments.
parser = argparse.ArgumentParser(prog='xenia-build')
# Grab all commands and populate the argument parser for each.
subparsers = parser.add_subparsers(title='subcommands',
dest='subcommand')
commands = discover_commands(subparsers)
# If the user passed no args, die nicely.
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
return
# Gather any arguments that we want to pass to child processes.
command_args = sys.argv[1:]
pass_args = []
try:
pass_index = command_args.index('--')
pass_args = command_args[pass_index + 1:]
command_args = command_args[:pass_index]
except:
pass
# Parse command name and dispatch.
args = vars(parser.parse_args(command_args))
command_name = args['subcommand']
try:
command = commands[command_name]
return_code = command.execute(args, pass_args, os.getcwd())
except Exception as e:
raise
return_code = 1
sys.exit(return_code)
# TODO(benvanik): move to build_tools utils module.
def import_vs_environment():
"""Finds the installed Visual Studio version and imports
interesting environment variables into os.environ.
Returns:
A version such as 2015 or None if no VS is found.
"""
version = 0
tools_path = ''
if 'VS140COMNTOOLS' in os.environ:
version = 2015
tools_path = os.environ['VS140COMNTOOLS']
if version == 0:
return None
tools_path = os.path.join(tools_path, '..\\..\\vc\\vcvarsall.bat')
args = [tools_path, '&&', 'set']
popen = subprocess.Popen(
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
variables, _ = popen.communicate()
envvars_to_save = (
'devenvdir',
'include',
'lib',
'libpath',
'path',
'pathext',
'systemroot',
'temp',
'tmp',
'windowssdkdir',
)
for line in variables.splitlines():
for envvar in envvars_to_save:
if re.match(envvar + '=', line.lower()):
var, setting = line.split('=', 1)
if envvar == 'path':
setting = os.path.dirname(sys.executable) + os.pathsep + setting
os.environ[var.upper()] = setting
break
os.environ['VSVERSION'] = str(version)
return version
def has_bin(bin):
"""Checks whether the given binary is present.
"""
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
exe_file = os.path.join(path, bin)
if os.path.isfile(exe_file) and os.access(exe_file, os.X_OK):
return True
exe_file = exe_file + '.exe'
if os.path.isfile(exe_file) and os.access(exe_file, os.X_OK):
return True
return None
def shell_call(command, throw_on_error=True, stdout_path=None):
"""Executes a shell command.
Args:
command: Command to execute, as a list of parameters.
throw_on_error: Whether to throw an error or return the status code.
stdout_path: File path to write stdout output to.
Returns:
If throw_on_error is False the status code of the call will be returned.
"""
stdout_file = None
if stdout_path:
stdout_file = open(stdout_path, 'w')
result = 0
try:
if throw_on_error:
result = 1
subprocess.check_call(command, shell=True, stdout=stdout_file)
result = 0
else:
result = subprocess.call(command, shell=True, stdout=stdout_file)
finally:
if stdout_file:
stdout_file.close()
return result
def run_premake(target_os, action):
"""Runs premake on the main project with the given format.
Args:
target_os: target --os to pass to premake.
action: action to preform.
"""
shell_call([
'python',
os.path.join('build_tools', 'premake'),
'--file=premake5.lua',
'--os=%s' % (target_os),
'--cc=clang',
'--test-suite-mode=combined',
'--verbose',
action,
])
def run_premake_clean():
"""Runs a premake clean operation.
"""
if sys.platform == 'darwin':
run_premake('macosx', 'clean')
elif sys.platform == 'win32':
run_premake('windows', 'clean')
else:
run_premake('linux', 'clean')
def run_platform_premake():
"""Runs all gyp configurations.
"""
if sys.platform == 'darwin':
run_premake('macosx', 'xcode')
elif sys.platform == 'win32':
run_premake('windows', 'vs2015')
else:
run_premake('linux', 'gmake')
def get_build_bin_path(args):
"""Returns the path of the bin/ path with build results based on the
configuration specified in the parsed arguments.
Args:
args: Parsed arguments.
Returns:
A full path for the bin folder.
"""
if sys.platform == 'darwin':
platform = 'macosx'
elif sys.platform == 'win32':
platform = 'windows'
else:
platform = 'linux'
return os.path.join(self_path, 'build', 'bin', platform, args['config'])
def discover_commands(subparsers):
"""Looks for all commands and returns a dictionary of them.
In the future commands could be discovered on disk.
Args:
subparsers: Argument subparsers parent used to add command parsers.
Returns:
A dictionary containing name-to-Command mappings.
"""
commands = {
'setup': SetupCommand(subparsers),
'pull': PullCommand(subparsers),
'premake': PremakeCommand(subparsers),
'build': BuildCommand(subparsers),
'gentests': GenTestsCommand(subparsers),
'test': TestCommand(subparsers),
'clean': CleanCommand(subparsers),
'nuke': NukeCommand(subparsers),
'lint': LintCommand(subparsers),
'format': FormatCommand(subparsers),
}
if sys.platform == 'win32':
commands['devenv'] = DevenvCommand(subparsers)
return commands
class Command(object):
"""Base type for commands.
"""
def __init__(self, subparsers, name, help_short=None, help_long=None,
*args, **kwargs):
"""Initializes a command.
Args:
subparsers: Argument subparsers parent used to add command parsers.
name: The name of the command exposed to the management script.
help_short: Help text printed alongside the command when queried.
help_long: Extended help text when viewing command help.
"""
self.name = name
self.help_short = help_short
self.help_long = help_long
self.parser = subparsers.add_parser(name,
help=help_short,
description=help_long)
self.parser.set_defaults(command_handler=self)
def execute(self, args, pass_args, cwd):
"""Executes the command.
Args:
args: Arguments hash for the command.
pass_args: Arguments list to pass to child commands.
cwd: Current working directory.
Returns:
Return code of the command.
"""
return 1
class SetupCommand(Command):
"""'setup' command."""
def __init__(self, subparsers, *args, **kwargs):
super(SetupCommand, self).__init__(
subparsers,
name='setup',
help_short='Setup the build environment.',
*args, **kwargs)
def execute(self, args, pass_args, cwd):
print('Setting up the build environment...')
print('')
# Setup submodules.
print('- git submodule init / update...')
shell_call([
'git',
'submodule',
'update',
'--init',
'--recursive',
])
print('')
print('- running premake...')
run_platform_premake()
print('')
print('Success!')
return 0
class PullCommand(Command):
"""'pull' command."""
def __init__(self, subparsers, *args, **kwargs):
super(PullCommand, self).__init__(
subparsers,
name='pull',
help_short='Pulls the repo and all dependencies and rebases changes.',
*args, **kwargs)
self.parser.add_argument('--merge', action='store_true',
help='Merges on master instead of rebasing.')
def execute(self, args, pass_args, cwd):
print('Pulling...')
print('')
print('- switching to master...')
shell_call([
'git',
'checkout',
'master',
])
print('')
print('- pulling self...')
if args['merge']:
shell_call([
'git',
'pull',
])
else:
shell_call([
'git',
'pull',
'--rebase',
])
print('')
print('- pulling dependencies...')
shell_call([
'git',
'submodule',
'update',
'--init',
'--recursive',
])
print('')
print('- running premake...')
run_platform_premake()
print('')
print('Success!')
return 0
class PremakeCommand(Command):
"""'premake' command."""
def __init__(self, subparsers, *args, **kwargs):
super(PremakeCommand, self).__init__(
subparsers,
name='premake',
help_short='Runs premake to update all projects.',
*args, **kwargs)
def execute(self, args, pass_args, cwd):
print('Running premake...')
print('')
# Update premake.
run_platform_premake()
print('Success!')
return 0
class BaseBuildCommand(Command):
"""Base command for things that require building."""
def __init__(self, subparsers, *args, **kwargs):
super(BaseBuildCommand, self).__init__(
subparsers,
*args, **kwargs)
self.parser.add_argument(
'--config', choices=['checked', 'debug', 'release'], default='debug',
help='Chooses the build configuration.')
self.parser.add_argument(
'--target', action='append', default=[],
help='Builds only the given target(s).')
self.parser.add_argument(
'--force', action='store_true',
help='Forces a full rebuild.')
def execute(self, args, pass_args, cwd):
print('- running premake...')
run_platform_premake()
print('')
print('- building (%s):%s...' % (
'all' if not len(args['target']) else ' '.join(args['target']),
args['config']))
if sys.platform == 'win32':
result = shell_call([
'devenv',
'/nologo',
'build/xenia.sln',
'/rebuild' if args['force'] else '/build',
args['config'],
] + [('/project ', target) for target in args['target']] +
pass_args, throw_on_error=False)
else:
# TODO(benvanik): other platforms.
print('ERROR: don\'t know how to build on this platform.')
print('')
if result != 0:
print('ERROR: build failed with one or more errors.')
return result
return 0
class BuildCommand(BaseBuildCommand):
"""'build' command."""
def __init__(self, subparsers, *args, **kwargs):
super(BuildCommand, self).__init__(
subparsers,
name='build',
help_short='Builds the project.',
*args, **kwargs)
def execute(self, args, pass_args, cwd):
print('Building %s...' % (args['config']))
print('')
result = super(BuildCommand, self).execute(args, pass_args, cwd)
if not result:
print('Success!')
return result
class TestCommand(BaseBuildCommand):
"""'test' command."""
def __init__(self, subparsers, *args, **kwargs):
super(TestCommand, self).__init__(
subparsers,
name='test',
help_short='Runs automated tests that have been built with `xb build`.',
help_long='''
To pass arguments to the test executables separate them with `--`.
For example, you can run only the instr_foo.s tests with:
$ xb test -- instr_foo
''',
*args, **kwargs)
self.parser.add_argument(
'--no-build', action='store_true',
help='Don\'t build before running tests.')
self.parser.add_argument(
'--continue', action='store_true',
help='Don\'t stop when a test errors, but continue running all.')
def execute(self, args, pass_args, cwd):
print('Testing...')
print('')
# The test executables that will be built and run.
test_targets = args['target'] or [
'xenia-cpu-frontend-tests',
]
args['target'] = test_targets
# Build all targets (if desired).
if not args['no_build']:
result = super(TestCommand, self).execute(args, [], cwd)
if result:
print('Failed to build, aborting test run.')
return result
# Ensure all targets exist before we run.
test_executables = [
os.path.join(get_build_bin_path(args), test_target)
for test_target in test_targets]
for test_executable in test_executables:
if not has_bin(test_executable):
print('ERROR: Unable to find %s - build it.' % (test_executable))
return 1
# Run tests.
any_failed = False
for test_executable in test_executables:
print('- %s' % (test_executable))
result = shell_call([
test_executable,
] + pass_args,
throw_on_error=False)
if result:
any_failed = True
if args['continue']:
print('ERROR: test failed but continuing due to --continue.')
else:
print('ERROR: test failed, aborting, use --continue to keep going.')
return result
if any_failed:
print('ERROR: one or more tests failed.')
result = 1
return result
class GenTestsCommand(Command):
"""'gentests' command."""
def __init__(self, subparsers, *args, **kwargs):
super(GenTestsCommand, self).__init__(
subparsers,
name='gentests',
help_short='Generates test binaries.',
help_long='''
Generates test binaries (under src/xenia/cpu/frontend/testing/bin/).
Run after modifying test .s files.
''',
*args, **kwargs)
def execute(self, args, pass_args, cwd):
print('Generating test binaries...')
print('')
binutils_path = os.path.join('third_party', 'binutils-ppc-cygwin')
ppc_as = os.path.join(binutils_path, 'powerpc-none-elf-as')
ppc_ld = os.path.join(binutils_path, 'powerpc-none-elf-ld')
ppc_objdump = os.path.join(binutils_path, 'powerpc-none-elf-objdump')
ppc_nm = os.path.join(binutils_path, 'powerpc-none-elf-nm')
test_src = os.path.join('src', 'xenia', 'cpu', 'frontend', 'testing')
test_bin = os.path.join(test_src, 'bin')
# Ensure the test output path exists.
if not os.path.exists(test_bin):
os.mkdir(test_bin)
src_files = [os.path.join(root, name)
for root, dirs, files in os.walk('src')
for name in files
if name.endswith(('.s'))]
def make_unix_path(p):
"""Forces a unix path separator style, as required by binutils.
"""
return string.replace(p, os.sep, '/')
any_errors = False
for src_file in src_files:
print('- %s' % (src_file))
src_name = os.path.splitext(os.path.basename(src_file))[0]
obj_file = os.path.join(test_bin, src_name) + '.o'
shell_call([
ppc_as,
'-a32',
'-be',
'-mregnames',
'-mpower7',
'-maltivec',
'-mvsx',
'-mvmx128',
'-R',
'-o%s' % (make_unix_path(obj_file)),
make_unix_path(src_file),
])
dis_file = os.path.join(test_bin, src_name) + '.dis'
shell_call([
ppc_objdump,
'--adjust-vma=0x100000',
'-Mpower7',
'-Mvmx128',
'-D',
'-EB',
make_unix_path(obj_file),
], stdout_path=dis_file)
# Eat the first 4 lines to kill the file path that'll differ across machines.
with open(dis_file) as f:
dis_file_lines = f.readlines()
with open(dis_file, 'w') as f:
f.writelines(dis_file_lines[4:])
shell_call([
ppc_ld,
'-A powerpc:common32',
'-melf32ppc',
'-EB',
'-nostdlib',
'--oformat=binary',
'-Ttext=0x80000000',
'-e0x80000000',
'-o%s' % (make_unix_path(os.path.join(test_bin, src_name) + '.bin')),
make_unix_path(obj_file),
])
shell_call([
ppc_nm,
'--numeric-sort',
make_unix_path(obj_file),
], stdout_path=os.path.join(test_bin, src_name) + '.map')
if any_errors:
print('ERROR: failed to build one or more tests.')
return 1
return 0
class CleanCommand(Command):
"""'clean' command."""
def __init__(self, subparsers, *args, **kwargs):
super(CleanCommand, self).__init__(
subparsers,
name='clean',
help_short='Removes intermediate files and build outputs.',
*args, **kwargs)
def execute(self, args, pass_args, cwd):
print('Cleaning build artifacts...')
print('')
print('- premake clean...')
run_premake_clean()
print('')
print('Success!')
return 0
class NukeCommand(Command):
"""'nuke' command."""
def __init__(self, subparsers, *args, **kwargs):
super(NukeCommand, self).__init__(
subparsers,
name='nuke',
help_short='Removes all build/ output.',
*args, **kwargs)
def execute(self, args, pass_args, cwd):
print('Cleaning build artifacts...')
print('')
print('- removing build/...')
if os.path.isdir('build/'):
shutil.rmtree('build/')
print('')
print('- git reset to master...')
shell_call([
'git',
'checkout',
'--hard',
'master',
])
print('')
print('- running premake...')
run_platform_premake()
print('')
print('Success!')
return 0
class LintCommand(Command):
"""'lint' command."""
def __init__(self, subparsers, *args, **kwargs):
super(LintCommand, self).__init__(
subparsers,
name='lint',
help_short='Checks for lint errors with clang-format.',
*args, **kwargs)
self.parser.add_argument(
'--all', action='store_true',
help='Lint all files, not just those changed.')
self.parser.add_argument(
'--origin', action='store_true',
help='Lints all files changed relative to origin/master.')
def execute(self, args, pass_args, cwd):
clang_format_binary = 'clang-format'
win32_binary = 'C:\\Program Files (x86)\\LLVM\\bin\\clang-format.exe'
if os.path.exists(win32_binary):
clang_format_binary = win32_binary
if not has_bin(clang_format_binary):
print 'ERROR: clang-format is not on PATH'
print 'LLVM is available from http://llvm.org/releases/download.html'
print 'See docs/style_guide.md for instructions on how to get it'
return 1
difftemp = '.difftemp.txt'
if args['all']:
all_files = [os.path.join(root, name)
for root, dirs, files in os.walk('src')
for name in files
if name.endswith(('.cc', '.c', '.h', '.inl'))]
print('- linting %d files' % (len(all_files)))
any_errors = False
for file_path in all_files:
if os.path.exists(difftemp): os.remove(difftemp)
ret = shell_call([
clang_format_binary,
'-output-replacements-xml',
'-style=file',
file_path,
], throw_on_error=False, stdout_path=difftemp)
with open(difftemp) as f:
had_errors = '<replacement ' in f.read()
if os.path.exists(difftemp): os.remove(difftemp)
if had_errors:
any_errors = True
print('')
print(file_path)
shell_call([
clang_format_binary,
'-style=file',
file_path,
], throw_on_error=False, stdout_path=difftemp)
shell_call([
'python',
'tools/diff.py',
file_path,
difftemp,
difftemp,
])
shell_call([
'type',
difftemp,
])
if os.path.exists(difftemp): os.remove(difftemp)
print('')
print('')
if any_errors:
print('ERROR: 1+ diffs. Stage changes and run \'xb format\' to fix.')
return 1
else:
print('Linting completed successfully.')
return 0
else:
print('- git-clang-format --diff')
if os.path.exists(difftemp): os.remove(difftemp)
ret = shell_call([
'python',
'third_party/clang-format/git-clang-format',
'--binary=%s' % (clang_format_binary),
'--commit=%s' % ('origin/master' if args['origin'] else 'HEAD'),
'--diff',
], throw_on_error=False, stdout_path=difftemp)
with open(difftemp) as f:
not_modified = 'no modified files' in f.read()
f.close()
if os.path.exists(difftemp): os.remove(difftemp)
if not not_modified:
any_errors = True
print('')
shell_call([
'python',
'third_party/clang-format/git-clang-format',
'--binary=%s' % (clang_format_binary),
'--commit=%s' % ('origin/master' if args['origin'] else 'HEAD'),
'--diff',
])
print('ERROR: 1+ diffs. Stage changes and run \'xb format\' to fix.')
return 1
else:
print('Linting completed successfully.')
return 0
class FormatCommand(Command):
"""'format' command."""
def __init__(self, subparsers, *args, **kwargs):
super(FormatCommand, self).__init__(
subparsers,
name='format',
help_short='Reformats staged code with clang-format.',
*args, **kwargs)
self.parser.add_argument(
'--all', action='store_true',
help='Format all files, not just those changed.')
self.parser.add_argument(
'--origin', action='store_true',
help='Formats all files changed relative to origin/master.')
def execute(self, args, pass_args, cwd):
clang_format_binary = 'clang-format'
win32_binary = 'C:\\Program Files (x86)\\LLVM\\bin\\clang-format.exe'
if os.path.exists(win32_binary):
clang_format_binary = win32_binary
if not has_bin(clang_format_binary):
print 'ERROR: clang-format is not on PATH'
print 'LLVM is available from http://llvm.org/releases/download.html'
print 'See docs/style_guide.md for instructions on how to get it'
return 1
if args['all']:
all_files = [os.path.join(root, name)
for root, dirs, files in os.walk('src/')
for name in files
if name.endswith(('.cc', '.c', '.h', '.inl'))]
print('- clang-format [%d files]' % (len(all_files)))
any_errors = False
for file_path in all_files:
ret = shell_call([
clang_format_binary,
'-i',
'-style=file',
file_path,
], throw_on_error=False)
if ret:
any_errors = True
print('')
if any_errors:
print('ERROR: 1+ clang-format calls failed.')
print('Ensure all files are staged.')
return 1
else:
print('Formatting completed successfully.')
return 0
else:
print('- git-clang-format')
shell_call([
'python',
'third_party/clang-format/git-clang-format',
'--binary=%s' % (clang_format_binary),
'--commit=%s' % ('origin/master' if args['origin'] else 'HEAD'),
])
print('')
return 0
class DevenvCommand(Command):
"""'devenv' command."""
def __init__(self, subparsers, *args, **kwargs):
super(DevenvCommand, self).__init__(
subparsers,
name='devenv',
help_short='Launches Visual Studio with the sln.',
*args, **kwargs)
def execute(self, args, pass_args, cwd):
print('Launching Visual Studio...')
print('')
print('- running premake...')
run_platform_premake()
print('')
print('- launching devenv...')
shell_call([
'devenv',
'build\\xenia.sln',
])
print('')
return 0
if __name__ == '__main__':
main()

5
xeniarc Normal file
View File

@ -0,0 +1,5 @@
# Copyright 2015 Ben Vanik. All Rights Reserved.
#
# Useful bash aliases and completions.
alias xb='python xenia-build.py'