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
git clone https://github.com/benvanik/xenia.git
./xenia-build.sh setup
./xenia-build.sh build
./bin/xenia some.xex
cd xenia && source xeniarc
xb setup
xb build
./bin/xenia-run some.xex
## Building
### Requirements
## Requirements
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
@ -43,8 +42,85 @@ Windows SDK, the full DirectX SDK, and the Kinect SDK.
#### 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'