Skip to content

Commit

Permalink
Add sources command
Browse files Browse the repository at this point in the history
  • Loading branch information
adamrehn committed Aug 13, 2020
1 parent b5f020f commit 9a054b8
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 9 deletions.
1 change: 1 addition & 0 deletions conan_ue4cli/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
from .build import build
from .generate import generate
from .precompute import precompute
from .sources import sources
from .update import update
11 changes: 4 additions & 7 deletions conan_ue4cli/commands/precompute.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import argparse, glob, json, os, subprocess, shutil, sys, tempfile
import argparse, glob, json, os, sys, tempfile
from os.path import abspath, exists, join
from ..common import ConanTools, LibraryResolver, ProfileManagement, Utility
from ..common import ConanTools, LibraryResolver, PackageManagement, ProfileManagement, Utility


# Retrieves the Unreal Engine module name for a third-party library wrapper package
Expand Down Expand Up @@ -51,11 +51,8 @@ def precompute(manager, argv):
# Create an auto-deleting temporary directory to hold our Conan build output
with tempfile.TemporaryDirectory() as tempDir:

# Run `conan install` to install the dependencies for the target profile and generate our JSON dependency info
subprocess.run(['conan', 'install', args.dir, '--profile=' + args.profile, '-g=json'], cwd=tempDir, check=True)

# Parse the JSON dependency info
info = json.loads(Utility.readFile(join(tempDir, 'conanbuildinfo.json')))
# Run `conan install` to install the dependencies for the target profile and retrieve the JSON dependency info
info = PackageManagement.getBuildJson(args.dir, args.profile)

# Create an include directory for our aggregated headers
includeDir = join(args.dir, 'precomputed', engineVersion, targetID, 'include')
Expand Down
64 changes: 64 additions & 0 deletions conan_ue4cli/commands/sources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import argparse, glob, itertools, os, shutil, subprocess, sys, tempfile
from os.path import abspath, exists, join
from ..common import ConanTools, PackageManagement, ProfileManagement, RecipeManagement, Utility

def sources(manager, argv):

# Our supported command-line arguments
parser = argparse.ArgumentParser(
prog='ue4 conan sources',
description = 'Retrieves the source code of the dependencies for one or more conanfiles'
)
parser.add_argument('-d', '-dir', dest='dir', metavar='DIR', default=os.getcwd(), help='Specifies output directory where source archives should be generated (defaults to the current working directory)')
parser.add_argument('profile', metavar='profile', choices=ProfileManagement.listGeneratedProfiles(False) + ['host'], help='The Conan profile to use when retrieving conanfile dependencies')
parser.add_argument('conanfile', nargs='+', help='Paths (or glob patterns) specifying one or more conanfiles to process')

# Parse the supplied command-line arguments
args = parser.parse_args(argv)

# If the user specified "host" as the profile then use the default profile for the host platform
if args.profile == 'host':
args.profile = ProfileManagement.profileForHostPlatform(manager)
print('Using profile for host platform "{}"'.format(args.profile))

# Verify that at least one conanfile was specified and that all specified conanfiles actually exist
conanfiles = itertools.chain.from_iterable([glob.glob(p, recursive=True) if '*' in p else [p] for p in args.conanfile])
conanfiles = list([abspath(p) for p in conanfiles])
for conanfile in conanfiles:
if not exists(conanfile):
print('Error: the file "{}" does not exist'.format(conanfile))
sys.exit(1)

# Aggregate the dependencies from all specified conanfiles
dependencies = list(itertools.chain.from_iterable([
PackageManagement.getDependencyGraph(conanfile, args.profile)
for conanfile in conanfiles
]))

# Filter out any wrapper packages in the list of dependencies
dependencies = list([
dep for dep in dependencies
if dep['is_ref'] is True and RecipeManagement.parseReference(dep['reference'])['version'] not in ['ue4']
])

# Retrieve the source code for each dependency in turn
for dependency in dependencies:

# Create an auto-deleting temporary directory to hold the conanfile and uncompressed source code
with tempfile.TemporaryDirectory() as tempDir:

# Retrieve the conanfile for the dependency
recipe, _ = Utility.run(['conan', 'get', '--raw', dependency['reference']], check=True)
conanfile = join(tempDir, 'conanfile.py')
ConanTools.save(conanfile, recipe)

# Retrieve the source code for the dependency
sourceDir = join(tempDir, 'source')
subprocess.run(['conan', 'source', conanfile, '-sf', sourceDir], check=True)

# Compress the source code and place the archive in our output directory
details = RecipeManagement.parseReference(dependency['reference'])
shutil.make_archive(join(args.dir, '{}-{}'.format(details['name'], details['version'])), 'zip', sourceDir)

# Inform the user that source code archival is complete
print('Done.')
37 changes: 36 additions & 1 deletion conan_ue4cli/common/PackageManagement.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os
import json, os, subprocess, tempfile
from os.path import join
from .ConanTools import ConanTools
from .Utility import Utility
Expand All @@ -25,6 +25,41 @@ def generateWrapper(libName, template, delegates, packageDir, channel, profile):
ConanTools.save(join(packageDir, 'conanfile.py'), conanfile)
PackageManagement.install(packageDir, channel, profile)

@staticmethod
def getBuildJson(conanfile, profile):
'''
Installs the dependencies for a consumer conanfile and parses the generated `conanbuildinfo.json` file.
Calls the `conan install` command internally.
'''

# Create an auto-deleting temporary directory to hold our Conan build output
with tempfile.TemporaryDirectory() as tempDir:

# Run `conan install` to install the dependencies for the target profile and generate our JSON dependency info
subprocess.run(['conan', 'install', conanfile, '--profile=' + profile, '-g=json'], cwd=tempDir, check=True)

# Parse the JSON dependency info
return json.loads(Utility.readFile(join(tempDir, 'conanbuildinfo.json')))

@staticmethod
def getDependencyGraph(conanfile, profile):
'''
Retrieves the dependency graph for a consumer conanfile without installing dependency packages.
Calls the `conan info` command internally.
'''

# Create an auto-deleting temporary directory to hold our Conan info output
with tempfile.TemporaryDirectory() as tempDir:

# Run `conan info` to generate the JSON for the dependency graph
jsonFile = join(tempDir, 'dependencies.json')
subprocess.run(['conan', 'info', conanfile, '-pr', profile, '--json', jsonFile], check=True)

# Parse the JSON dependency graph
return json.loads(Utility.readFile(jsonFile))

@staticmethod
def removeBasePackages():
'''
Expand Down
6 changes: 5 additions & 1 deletion conan_ue4cli/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .commands import boilerplate, build, generate, precompute, update
from .commands import boilerplate, build, generate, precompute, sources, update
import os, platform, sys

def main(manager, args):
Expand All @@ -25,6 +25,10 @@ def main(manager, args):
'function': precompute,
'description': 'Generates precomputed dependency data for UE4 boilerplate modules'
},
'sources': {
'function': sources,
'description': 'Retrieves the source code of the dependencies for one or more conanfiles'
},
'update': {
'function': update,
'description': 'Caches the latest recipe data from the ue4-conan-recipes repo'
Expand Down

0 comments on commit 9a054b8

Please sign in to comment.