Initial project skeleton.

This includes a working gyp-based build of an executable that uses LLVM.
This commit is contained in:
Ben Vanik 2013-01-11 01:23:08 -08:00
parent c03f57c8c6
commit 7e4a9220d7
21 changed files with 920 additions and 9 deletions

68
.gitignore vendored Normal file
View File

@ -0,0 +1,68 @@
# ==============================================================================
# Misc system junk
# ==============================================================================
.DS_Store
._*
.Spotlight-V100
.Trashes
.com.apple.*
Thumbs.db
Desktop.ini
# ==============================================================================
# Projects/IDE files
# ==============================================================================
# Sublime Text
*.sublime-project
*.sublime-workspace
# VIM
.*.sw[a-z]
*.un~
Session.vim
# TextMate
*.tmproj
*.tmproject
tmtags
# Eclipse
.project
.metadata
# ==============================================================================
# Temp generated code
# ==============================================================================
*.py[co]
.coverage
# ==============================================================================
# Logs and dumps
# ==============================================================================
npm-debug.log
private/
# ==============================================================================
# Build system output
# ==============================================================================
# npm/node
.lock-wscript
node_modules/
node_modules/**/build/
node_modules/.bin/
# coverage/etc
scratch/
.anvil-cache
.build-cache/
build/
build-out/
build-gen/
build-bin/
build-test/

9
.gitmodules vendored Normal file
View File

@ -0,0 +1,9 @@
[submodule "third_party/ninja"]
path = third_party/ninja
url = git://github.com/martine/ninja.git
[submodule "third_party/llvm"]
path = third_party/llvm
url = http://llvm.org/git/llvm.git
[submodule "third_party/gyp"]
path = third_party/gyp
url = https://github.com/benvanik/gyp.git

6
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,6 @@
Contributors
------------
Names should be added to this file like so: `Name or Organization <email address>`
* Ben Vanik <ben.vanik@gmail.com>

View File

@ -15,13 +15,12 @@ Coming soon (maybe):
## Quickstart ## Quickstart
git clone https://github.com/benvanik/xenia.git git clone https://github.com/benvanik/xenia.git
./xenia-build.sh setup cd xenia && source xeniarc
./xenia-build.sh build xb setup
./bin/xenia some.xex xb build
./bin/xenia-run some.xex
## Building ## Requirements
### Requirements
You must have a 64-bit machine for building and running the project. Always You must have a 64-bit machine for building and running the project. Always
run your system updater before building and make sure you have the latest run your system updater before building and make sure you have the latest
@ -43,8 +42,85 @@ Windows SDK, the full DirectX SDK, and the Kinect SDK.
#### OS X #### OS X
* [Xcode 4](http://developer.apple.com/xcode/) Only tested on OS X 10.8 (Mountain Lion).
### Dependencies * [Xcode 4](http://developer.apple.com/xcode/) + command line tools
* [Homebrew](http://mxcl.github.com/homebrew/)
TODO ## Building
A simple build script is included to manage basic tasks such as building
dependencies.
./xenia-build.py --help
The `xeniarc` bash file has some aliases that save some keypresses.
source xeniarc
xb -> python xenia-build.py
xbb -> python xenia-build.py build
xbc -> python xenia-build.py clean
### Commands
#### setup
Run this on initial checkout to pull down all dependencies and setup LLVM.
xb setup
#### pull
Does a `git pull` in addition to updating submodules and rebuilding dependencies
and gyp outputs. Use this, if possible, instead of git pull.
xb pull
#### gyp
Updates all of the supported gyp projects. If you're using Visual Studio or
Xcode to build or debug your projects you'll need to run this after you change
gyp/gypi files.
xb gyp
#### build
Builds all xenia targets using ninja. Release is built by default; specify
`--debug` to build the debug configuration.
xb build
xb build --debug
#### clean
Cleans just xenia outputs from the build/ directory. A following build will just
have the rebuild xenia and not all of the dependencies.
xb clean
#### nuke
Cleans up xenia outputs as well as all dependencies. A full build will be
required after this - including LLVM - so only do this if you want to reclaim
your disk space or something is really wrong.
xb nuke
## Running
Use the wrapper shell scripts under `bin/` to run tools. They will ensure the
tools are built (but not that they are up to date) before running and allow
switching between the debug and release variants with `--debug`.
### xenia-info
Dumps information about a xex file.
./bin/xenia-info some.xex
### xenia-run
Runs a xex.
./bin/xenia-run some.xex

16
bin/xenia-info Executable file
View File

@ -0,0 +1,16 @@
#!/bin/sh
DIR="$( cd "$( dirname "$0" )" && pwd )"
CONFIG=release
case "$*" in
(*--debug*) CONFIG=debug;;
esac
EXEC=$DIR/../build/xenia/$CONFIG/xenia-info
if [ ! -f "$EXEC" ]; then
python $DIR/../xenia-build.py build --$CONFIG
fi
$EXEC "$@"

16
bin/xenia-run Executable file
View File

@ -0,0 +1,16 @@
#!/bin/sh
DIR="$( cd "$( dirname "$0" )" && pwd )"
CONFIG=release
case "$*" in
(*--debug*) CONFIG=debug;;
esac
EXEC=$DIR/../build/xenia/$CONFIG/xenia-run
if [ ! -f "$EXEC" ]; then
python $DIR/../xenia-build.py build --$CONFIG
fi
$EXEC "$@"

89
common.gypi Normal file
View File

@ -0,0 +1,89 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'default_configuration': 'release',
'variables': {
'configurations': {
'debug': {
},
'release': {
},
},
# LLVM paths.
'llvm_path': 'build/llvm/release/',
'llvm_config': '<(llvm_path)bin/llvm-config',
},
'target_defaults': {
'include_dirs': [
'include/',
],
'configurations': {
'debug': {
'defines': [
'DEBUG',
],
'msvs_configuration_attributes': {
'OutputDirectory': '<(DEPTH)\\build\\xenia\\debug',
},
'msvs_settings': {
'VCCLCompilerTool': {
'Optimization': '2',
'BasicRuntimeChecks': '0', # disable /RTC1 when compiling /O2
'DebugInformationFormat': '3',
'ExceptionHandling': '0',
'RuntimeTypeInfo': 'false',
'OmitFramePointers': 'false',
},
'VCLinkerTool': {
'LinkIncremental': '2',
'GenerateDebugInformation': 'true',
'StackReserveSize': '2097152',
},
},
},
'release': {
'defines': [
'RELEASE',
],
'msvs_configuration_attributes': {
'OutputDirectory': '<(DEPTH)\\build\\xenia\\release',
},
'msvs_settings': {
'VCCLCompilerTool': {
'Optimization': '2',
'InlineFunctionExpansion': '2',
'EnableIntrinsicFunctions': 'true',
'FavorSizeOrSpeed': '0',
'ExceptionHandling': '0',
'RuntimeTypeInfo': 'false',
'OmitFramePointers': 'false',
'StringPooling': 'true',
},
'VCLinkerTool': {
'LinkIncremental': '1',
'GenerateDebugInformation': 'true',
'OptimizeReferences': '2',
'EnableCOMDATFolding': '2',
'StackReserveSize': '2097152',
},
},
},
},
'msvs_configuration_platform': 'x64',
'msvs_cygwin_shell': '0',
'scons_settings': {
'sconsbuild_dir': '<(DEPTH)/build/xenia/',
},
'xcode_settings': {
'SYMROOT': '<(DEPTH)/build/xenia/',
'ARCHS': [ 'x86_64' ],
'GCC_TREAT_WARNINGS_AS_ERRORS': 'YES',
'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES',
},
},
}

15
include/xenia/xenia.h Normal file
View File

@ -0,0 +1,15 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_H_
#define XENIA_H_
int some_function(int x);
#endif // XENIA_H_

55
src/core/something.cc Normal file
View File

@ -0,0 +1,55 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/xenia.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
using namespace llvm;
int some_function(int xx) {
LLVMContext &context = getGlobalContext();
//IRBuilder<> builder(context);
Module *module = new Module("my cool jit", context);
Constant* c = module->getOrInsertFunction("mul_add",
/*ret type*/ IntegerType::get(context, 32),
/*args*/ IntegerType::get(context, 32),
IntegerType::get(context, 32),
IntegerType::get(context, 32),
/*varargs terminated with null*/ NULL);
Function* mul_add = cast<Function>(c);
mul_add->setCallingConv(CallingConv::C);
Function::arg_iterator args = mul_add->arg_begin();
Value* x = args++;
x->setName("x");
Value* y = args++;
y->setName("y");
Value* z = args++;
z->setName("z");
BasicBlock* block = BasicBlock::Create(getGlobalContext(), "entry", mul_add);
IRBuilder<> builder(block);
Value* tmp = builder.CreateBinOp(Instruction::Mul,
x, y, "tmp");
Value* tmp2 = builder.CreateBinOp(Instruction::Add,
tmp, z, "tmp2");
builder.CreateRet(tmp2);
module->dump();
delete module;
return xx + 4;
}

6
src/core/sources.gypi Normal file
View File

@ -0,0 +1,6 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'something.cc',
],
}

1
third_party/gyp vendored Submodule

@ -0,0 +1 @@
Subproject commit 12d97efcb0d2cbe321ab6f050c2b2311e7048429

1
third_party/llvm vendored Submodule

@ -0,0 +1 @@
Subproject commit b956ec176a23dff2324c4938c3433c5e5ce2eae5

1
third_party/ninja vendored Submodule

@ -0,0 +1 @@
Subproject commit 8a4c9e05f794162af46939e078ee6464367cafc9

7
tools/tools.gypi Normal file
View File

@ -0,0 +1,7 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'includes': [
'xenia-info/xenia-info.gypi',
'xenia-run/xenia-run.gypi',
],
}

View File

@ -0,0 +1,14 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/xenia.h>
int main() {
return some_function(4);
}

View File

@ -0,0 +1,21 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'targets': [
{
'target_name': 'xenia-info',
'type': 'executable',
'dependencies': [
'xeniacore',
],
'include_dirs': [
'.',
],
'sources': [
'xenia-info.cc',
],
},
],
}

View File

@ -0,0 +1,14 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/xenia.h>
int main() {
return some_function(4);
}

View File

@ -0,0 +1,21 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'targets': [
{
'target_name': 'xenia-run',
'type': 'executable',
'dependencies': [
'xeniacore',
],
'include_dirs': [
'.',
],
'sources': [
'xenia-run.cc',
],
},
],
}

413
xenia-build.py Executable file
View File

@ -0,0 +1,413 @@
#!/usr/bin/env python
# Copyright 2013 Ben Vanik. All Rights Reserved.
"""
"""
__author__ = 'ben.vanik@gmail.com (Ben Vanik)'
import os
import shutil
import subprocess
import sys
def main():
# Add self to the root search path.
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
# Check python version.
if sys.version_info < (2, 7):
print 'ERROR: python 2.7+ required'
sys.exit(1)
return
# Grab all commands.
commands = discover_commands()
# Parse command name and dispatch.
try:
if len(sys.argv) < 2:
raise ValueError('No command given')
command_name = sys.argv[1]
if not commands.has_key(command_name):
raise ValueError('Command "%s" not found' % (command_name))
command = commands[command_name]
return_code = run_command(command=command,
args=sys.argv[2:],
cwd=os.getcwd())
except ValueError:
print usage(commands)
return_code = 1
except Exception as e:
#print e
raise
return_code = 1
sys.exit(return_code)
def discover_commands():
"""Looks for all commands and returns a dictionary of them.
In the future commands could be discovered on disk.
Returns:
A dictionary containing name-to-Command mappings.
"""
commands = {
'setup': SetupCommand(),
'pull': PullCommand(),
'gyp': GypCommand(),
'build': BuildCommand(),
'clean': CleanCommand(),
'nuke': NukeCommand(),
}
return commands
def usage(commands):
"""Gets usage info that can be displayed to the user.
Args:
commands: A command dictionary from discover_commands.
Returns:
A string containing usage info and a command listing.
"""
s = 'xenia-build.py command [--help]\n'
s += '\n'
s += 'Commands:\n'
command_names = commands.keys()
command_names.sort()
for command_name in command_names:
s += ' %s\n' % (command_name)
command_help = commands[command_name].help_short
if command_help:
s += ' %s\n' % (command_help)
return s
def run_command(command, args, cwd):
"""Runs a command with the given context.
Args:
command: Command to run.
args: Arguments, with the app and command name stripped.
cwd: Current working directory.
Returns:
0 if the command succeeded and non-zero otherwise.
Raises:
ValueError: The command could not be found or was not specified.
"""
# TODO(benvanik): parse arguments/etc.
return command.execute(args, cwd)
def has_bin(bin):
"""Checks whether the given binary is present.
"""
DEVNULL = open(os.devnull, 'wb')
return True if subprocess.call(
'which %s' % (bin), shell=True, stdout=DEVNULL) == 0 else False
def shell_call(command):
"""Executes a shell command.
"""
subprocess.check_call(command, shell=True)
class Command(object):
"""Base type for commands.
"""
def __init__(self, name, help_short=None, help_long=None, *args, **kwargs):
"""Initializes a command.
Args:
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
def execute(self, args, cwd):
"""Executes the command.
Args:
args: Arguments list.
cwd: Current working directory.
Returns:
Return code of the command.
"""
return 1
def post_update_deps(config):
"""Runs common tasks that should be executed after any deps are changed.
Args:
config: 'debug' or 'release'.
"""
print '- building llvm...'
shell_call('third_party/ninja/ninja -C build/llvm/%s-obj/ install' % (config))
print ''
class SetupCommand(Command):
"""'setup' command."""
def __init__(self, *args, **kwargs):
super(SetupCommand, self).__init__(
name='setup',
help_short='Setup the build environment.',
*args, **kwargs)
def execute(self, args, cwd):
print 'Setting up the build environment...'
print ''
# Setup submodules.
print '- git submodule init / update...'
shell_call('git submodule init')
shell_call('git submodule update')
print ''
# LLVM needs to pull with --rebase.
# TODO(benvanik): any way to do this to just the submodule?
#shell_call('git config branch.master.rebase true')
# Disable core.filemode on Windows to prevent weird file mode diffs in git.
# TODO(benvanik): check cygwin test - may be wrong when using Windows python
if os.path.exists('/Cygwin.bat'):
print '- setting filemode off on cygwin...'
shell_call('git config core.filemode false')
shell_call('git submodule foreach git config core.filemode false')
print ''
# Run the ninja bootstrap to build it, if it's missing.
if not os.path.exists('third_party/ninja/ninja'):
print '- preparing ninja...'
shell_call('python third_party/ninja/bootstrap.py')
print ''
# Ensure cmake is present.
if not has_bin('cmake'):
print '- installing cmake...'
if has_bin('brew'):
shell_call('brew install cmake')
elif has_bin('apt-get'):
shell_call('sudo apt-get install cmake')
else:
print 'ERROR: need to install cmake, use:'
print 'http://www.cmake.org/cmake/resources/software.html'
return 1
print ''
# LLVM.
print '- preparing llvm...'
#generator = 'Visual Studio 10 Win64'
generator = 'Ninja'
def prepareLLVM(path, obj_path, mode):
os.chdir(cwd)
if not os.path.exists(path):
os.makedirs(path)
if not os.path.exists(obj_path):
os.makedirs(obj_path)
os.chdir(obj_path)
shell_call(' '.join([
'PATH=$PATH:../../../third_party/ninja/',
'cmake',
'-G"%s"' % (generator),
'-DCMAKE_INSTALL_PREFIX:STRING=../../../%s' % (path),
'-DCMAKE_BUILD_TYPE:STRING=%s' % (mode),
'-DLLVM_TARGETS_TO_BUILD:STRING="X86"',
'-DLLVM_INCLUDE_EXAMPLES:BOOL=OFF',
'-DLLVM_INCLUDE_TESTS:BOOL=OFF',
'../../../third_party/llvm/',
]))
os.chdir(cwd)
prepareLLVM('build/llvm/debug/', 'build/llvm/debug-obj/', 'Debug')
prepareLLVM('build/llvm/release/', 'build/llvm/release-obj/', 'Release')
print ''
post_update_deps('debug')
post_update_deps('release')
print '- running gyp...'
run_all_gyps()
print ''
print 'Success!'
return 0
class PullCommand(Command):
"""'pull' command."""
def __init__(self, *args, **kwargs):
super(PullCommand, self).__init__(
name='pull',
help_short='Pulls the repo and all dependencies.',
*args, **kwargs)
def execute(self, args, cwd):
print 'Pulling...'
print ''
print '- pulling self...'
shell_call('git pull')
print ''
print '- pulling dependencies...'
shell_call('git submodule update')
print ''
post_update_deps('debug')
post_update_deps('release')
print '- running gyp...'
run_all_gyps()
print ''
print 'Success!'
return 0
def run_gyp(format):
"""Runs gyp on the main project with the given format.
Args:
format: gyp -f value.
"""
shell_call(' '.join([
'third_party/gyp/gyp',
'-f %s' % (format),
# Set the VS version.
# TODO(benvanik): allow user to set?
'-G msvs_version=2010',
# Removes the out/ from ninja builds.
'-G output_dir=.',
'--depth=.',
'--generator-output=build/xenia/',
'xenia.gyp',
]))
def run_all_gyps():
"""Runs all gyp configurations.
"""
run_gyp('ninja')
run_gyp('xcode')
run_gyp('msvs')
class GypCommand(Command):
"""'gyp' command."""
def __init__(self, *args, **kwargs):
super(GypCommand, self).__init__(
name='gyp',
help_short='Runs gyp to update all projects.',
*args, **kwargs)
def execute(self, args, cwd):
print 'Running gyp...'
print ''
# Update GYP.
run_all_gyps()
print 'Success!'
return 0
class BuildCommand(Command):
"""'build' command."""
def __init__(self, *args, **kwargs):
super(BuildCommand, self).__init__(
name='build',
help_short='Builds the project.',
*args, **kwargs)
def execute(self, args, cwd):
# TODO(benvanik): add arguments:
# --force
debug = '--debug' in args
config = 'debug' if debug else 'release'
# If there's no LLVM we may have been cleaned - run setup again.
if not os.path.exists('build/llvm/'):
print 'Missing LLVM, running setup...'
shell_call('python xenia-build.py setup')
print ''
print 'Building %s...' % (config)
print ''
print '- running gyp for ninja...'
run_gyp('ninja')
print ''
print '- building xenia in %s...' % (config)
shell_call('third_party/ninja/ninja -C build/xenia/%s' % (config))
print ''
print 'Success!'
return 0
class CleanCommand(Command):
"""'clean' command."""
def __init__(self, *args, **kwargs):
super(CleanCommand, self).__init__(
name='clean',
help_short='Removes intermediate files and build output.',
*args, **kwargs)
def execute(self, args, cwd):
print 'Cleaning build artifacts...'
print ''
print '- removing build/xenia/...'
if os.path.isdir('build/xenia/'):
shutil.rmtree('build/xenia/')
print ''
print 'Success!'
return 0
class NukeCommand(Command):
"""'nuke' command."""
def __init__(self, *args, **kwargs):
super(NukeCommand, self).__init__(
name='nuke',
help_short='Removes all build/ output.',
*args, **kwargs)
def execute(self, args, cwd):
print 'Cleaning build artifacts...'
print ''
print '- removing build/...'
if os.path.isdir('build/'):
shutil.rmtree('build/')
print ''
print 'Success!'
return 0
if __name__ == '__main__':
main()

55
xenia.gyp Normal file
View File

@ -0,0 +1,55 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'includes': [
'common.gypi',
'tools/tools.gypi',
],
'targets': [
{
'target_name': 'xeniacore',
'product_name': 'xeniacore',
'type': 'static_library',
'direct_dependent_settings': {
'include_dirs': [
'include/',
],
'link_settings': {
'libraries': [
'<!@(<(llvm_config) --ldflags)',
'<!@(<(llvm_config) --libs core)',
],
'library_dirs': [
# NOTE: this doesn't actually do anything...
# http://code.google.com/p/gyp/issues/detail?id=130
'<!@(<(llvm_config) --libdir)',
],
},
'linkflags': [
],
'libraries': [
#'!@(pkg-config --libs-only-l apr-1)',
],
},
'cflags': [
'<!@(<(llvm_config) --cxxflags)'
],
'defines': [
'__STDC_LIMIT_MACROS=1',
'__STDC_CONSTANT_MACROS=1',
],
'include_dirs': [
'.',
'<!@(<(llvm_config) --includedir)',
],
'includes': [
'src/core/sources.gypi',
],
},
],
}

7
xeniarc Normal file
View File

@ -0,0 +1,7 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
# Bash aliases
alias xb='python xenia-build.py'
alias xbb='python xenia-build.py build'
alias xbc='python xenia-build.py clean'