diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..1ebcdc1a1 --- /dev/null +++ b/.travis.yml @@ -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 diff --git a/README.md b/README.md index 98e4165b8..874d57b96 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/build_tools b/build_tools index ff0bab3c8..13552a894 160000 --- a/build_tools +++ b/build_tools @@ -1 +1 @@ -Subproject commit ff0bab3c8e3579684b0a5634d97f181bca8d0543 +Subproject commit 13552a8942c1fa5527e80698516912ad08d9e1ab diff --git a/src/xenia/base/main_win.cc b/src/xenia/base/main_win.cc index 3d3333075..a3c09916e 100644 --- a/src/xenia/base/main_win.cc +++ b/src/xenia/base/main_win.cc @@ -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 diff --git a/src/xenia/cpu/stack_walker_win.cc b/src/xenia/cpu/stack_walker_win.cc index d831bbfb8..9681e448d 100644 --- a/src/xenia/cpu/stack_walker_win.cc +++ b/src/xenia/cpu/stack_walker_win.cc @@ -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( + GetProcAddress(module, "SymGetOptions")); + sym_set_options_ = reinterpret_cast( + GetProcAddress(module, "SymSetOptions")); + sym_initialize_ = reinterpret_cast( + GetProcAddress(module, "SymInitialize")); + stack_walk_64_ = + reinterpret_cast(GetProcAddress(module, "StackWalk64")); + sym_function_table_access_64_ = reinterpret_cast( + GetProcAddress(module, "SymFunctionTableAccess64")); + sym_get_module_base_64_ = reinterpret_cast( + GetProcAddress(module, "SymGetModuleBase64")); + sym_get_sym_from_addr_64_ = reinterpret_cast( + 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 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( - GetProcAddress(module, "SymGetOptions")); - sym_set_options_ = reinterpret_cast( - GetProcAddress(module, "SymSetOptions")); - sym_initialize_ = reinterpret_cast( - GetProcAddress(module, "SymInitialize")); - stack_walk_64_ = - reinterpret_cast(GetProcAddress(module, "StackWalk64")); - sym_function_table_access_64_ = - reinterpret_cast( - GetProcAddress(module, "SymFunctionTableAccess64")); - sym_get_module_base_64_ = reinterpret_cast( - GetProcAddress(module, "SymGetModuleBase64")); - sym_get_sym_from_addr_64_ = reinterpret_cast( - 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, diff --git a/xb.bat b/xb.bat index f28135805..55cda3eb8 100644 --- a/xb.bat +++ b/xb.bat @@ -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 "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 diff --git a/xenia-build b/xenia-build new file mode 100644 index 000000000..891f95bbf --- /dev/null +++ b/xenia-build @@ -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 = '