Permalink
Browse files

A working version of backend packager

  • Loading branch information...
1 parent 6191d84 commit bbfde45d9d1a7f750c14a6688eef29c548dd0045 @andreyvit andreyvit committed Sep 23, 2013
View
@@ -44,14 +44,16 @@ Building LiveReload for Windows
Building LiveReload for Mac
---------------------------
-Note: right now this branch is in a transitional state; I'm doing active development of Mac v2.4.x. See the ‘master’ branch for the latest stable Mac version (v2.3.x).
+Note: right now this branch is in a transitional state; I'm doing active development of Mac v3.0.x. See the ‘master’ branch for the latest stable Mac version (v2.3.x).
Prerequisites:
-* Xcode 4.6
+* Xcode 5.0
* Node 0.10
-* IcedCoffeeScript 1.3.x: `npm install -g iced-coffee-script`
-* Ruby 1.8.7 for running Rake
+* CoffeeScript 1.6.x: `npm install -g coffee-script`
+* underscore.js: `npm install -g underscore`
+* grunt
+* optionally, Ruby 1.8.7 with Rake for running Rake tasks
For running tests:
@@ -63,25 +65,24 @@ Building:
1. Don’t forget to pull all submodules after getting the source code.
-1. Prepare the backend:
+1. Install and compile the backend modules:
- cd node_modules/livereload-new
- npm install
- coffee -c lib test
- mocha
- cucumber.js
- mocha
- cucumber.js
-
- Or use the watch mode for the latter commands:
+ cd node_modules/livereload-new && npm install && grunt &&
+ cd ../livereload-service-dummy && npm install && grunt
+ cd ../livereload-service-server && npm install && grunt
+ cd ../livereload-service-reloader && npm install && grunt
+ cd ../livereload-soa && npm install && grunt
+ cd ../livereload-server && coffee -c lib
+ cd ../livereload-protocol && coffee -c lib
+ cd ../pathspec && coffee -c lib
- coffee -cw lib test
- fsmonitor -s '+*.js' '!node_modules' '!.git' mocha -G -R spec
- fsmonitor -s '+*.js' '+features/**/*.coffee' '+*.feature' '!node_modules' '!.git' cucumber.js
+1. Package the backend modules into `mac/backend`:
-1. CURRENTLY BROKEN: Run `rake backend` to package the backend into `interim/backend`.
+ cd scripts && npm install
+ cd ..
+ scripts/node_modules/.bin/_coffee scripts/package-backend.coffee_ node_modules/livereload-new mac/backend
- For now (and during develpment) use override mode instead — open Xcode scheme settings and set LRBackendOverride env var to the full path of `node_modules/livereload-new/bin/livereload.js`.)
+ During development, use override mode instead — open Xcode scheme settings and set LRBackendOverride env var to the full path of `node_modules/livereload-new/bin/livereload.js`.
1. Open `LiveReload/LiveReload.xcodeproj` and build it with Xcode.
@@ -6,6 +6,9 @@
"scripts": {},
"dependencies": {
"livereload-soa": "0.0.x",
+ "livereload-service-dummy": "0.0.x",
+ "livereload-service-server": "0.0.x",
+ "livereload-service-reloader": "0.0.x",
"debug": "~0.7.0"
},
"devDependencies": {
@@ -1,5 +1,5 @@
{
- "name": "livereload-service-dumy",
+ "name": "livereload-service-dummy",
"author": "Andrey Tarantsov <andrey@tarantsov.com>",
"version": "0.0.1",
"description": "Part of LiveReload",
@@ -1,13 +1,14 @@
{
- "name": "livereload-service-browser",
+ "name": "livereload-service-reloader",
"author": "Andrey Tarantsov <andrey@tarantsov.com>",
"version": "0.0.1",
"description": "Part of LiveReload",
"scripts": {},
"main": "lib/reloader.js",
"dependencies": {
"livereload-soa": "0.0.x",
- "livereload-server": ">=0.2.3 <0.3"
+ "livereload-server": ">=0.2.3 <0.3",
+ "pathspec": ">=0.9.3"
},
"devDependencies": {
"grunt": "~0.4.1",
@@ -7,6 +7,8 @@ which = require 'which'
zlib = require 'zlib'
tar = require 'tar'
events = require 'events'
+semver = require 'semver'
+U = require 'underscore'
EXITCODE_INVALID_CMDLINE = 10
EXITCODE_ENV_ERROR = 9
@@ -41,16 +43,23 @@ class Package
@manifest = null
@name = 'UNKNOWN'
@localRepository = null
+ @family = null
+ @dependencies = []
@queued = no
@processed = no
@destinationDir = null
- @destinationDependenciesDir = null
+ @localDependenciesDir = null
loadManifest: (_) ->
unless @manifest
@manifest = JSON.parse(fs.readFile(@manifestFile, 'utf8', _))
@name = @manifest.name or throw new Error("Missing 'name' key in manifest file #{@manifestFile}")
+ @version = @manifest.version or throw new Error("Missing 'version' key in manifest file #{@manifestFile}")
+ unless (@version = semver.valid(@version))
+ throw new Error("Invalid 'version' key in manifest file #{@manifestFile}")
+ @family = @packager.obtainPackageFamily(@name)
+ @family.addLocalSource(this)
@tgzName = "#{@manifest.name}-#{@manifest.version}.tgz"
@tgzPath = Path.join(@dir, @tgzName)
return @manifest
@@ -59,13 +68,38 @@ class Package
@localRepository or= @packager.obtainModuleLocalRepository(@dir, _)
return @localRepository.findPackage(packageName, _)
+ addDependency: (requirement) ->
+ @dependencies.push(requirement)
+ @packager.obtainPackageFamily(requirement.name).addRequirement(requirement)
+
setDestinationDir: (dir) ->
@destinationDir = dir
- @destinationDependenciesDir = Path.join(dir, 'node_modules')
+ @localDependenciesDir = Path.join(dir, 'node_modules')
+
+ setDestinationParentDir: (dir) ->
+ @setDestinationDir(Path.join(dir, @name))
+
+
+class PackageRequirement
+ constructor: (@requiringPackage, @name, @versionSpec) ->
+ @resolvedPackage = null
+
+ if @versionSpec is 'latest'
+ @versionSpec = '*'
- setDestinationDependenciesDir: (dir) ->
- @destinationDependenciesDir = dir
- @destinationDir = Path.join(dir, @name)
+
+class PackageFamily
+ constructor: (@packager, @name) ->
+ @requirements = []
+ @localSources = []
+ @resolvedSources = []
+
+ addRequirement: (requirement) ->
+ @requirements.push requirement
+
+ addLocalSource: (source) ->
+ if @localSources.indexOf(source) < 0
+ @localSources.push(source)
class LocalPackageRepository
@@ -92,32 +126,39 @@ class Packager
@namesToPackageFamilies = {}
@errors = []
- @packagesToProcess = []
+ @selectedFamilies = []
obtainPackageFamily: (name) ->
if @namesToPackageFamilies.hasOwnProperty(name)
@namesToPackageFamilies[name]
else
- @namesToPackageFamilies[name] = new PackageFamily(this, name)
+ family = new PackageFamily(this, name)
+ @selectedFamilies.push(family)
+ @namesToPackageFamilies[name] = family
obtainPackage: (dir, _) ->
# console.log "obtainPackage(%s)", dir
if @pathsToPackages.hasOwnProperty(dir)
@pathsToPackages[dir]
else
- @pathsToPackages[dir] = pkg = if fsExists(dir, _) then new Package(this, dir) else null
- if pkg
+ if fsExists(dir, _)
+ pkg = new Package(this, dir)
pkg.loadManifest(_)
+ else
+ pkg = null
+ @pathsToPackages[dir] = pkg
return pkg
+
obtainFolderRepository: (path, _) ->
if @pathsToRepositories.hasOwnProperty(path)
@pathsToRepositories[path]
else
@pathsToRepositories[path] = if fsExists(path, _) then new LocalPackageRepository(this, path) else null
+
obtainModuleLocalRepository: (moduleFolder, _) ->
repositories = []
while true
@@ -130,53 +171,119 @@ class Packager
return new CompoundRepository(repositories)
- addPackageFolder: (sourceDir, destinationDir, _) ->
- pkg = @obtainPackage(sourceDir, _)
- if pkg
- pkg.setDestinationDir(destinationDir)
- @addPackage(pkg, _)
- else
- @addError "Missing source folder #{sourceDir}"
addPackage: (pkg, _) ->
return if pkg.queued
pkg.queued = yes
- @packagesToProcess.push pkg
- console.log "ENQUEUED: #{Path.dirname(pkg.dir)}: #{pkg.manifest.name}@#{pkg.manifest.version}"
+ console.log "PARSE: #{Path.dirname(pkg.dir)}: #{pkg.manifest.name}@#{pkg.manifest.version}"
for own depPackageName, depVersionSpec of pkg.manifest.dependencies || {}
- @addDependency(pkg, depPackageName, depVersionSpec, _)
-
- addDependency: (parentPackage, packageName, versionSpec, _) ->
+ requirement = new PackageRequirement(pkg, depPackageName, depVersionSpec)
+ pkg.addDependency(requirement)
+ @resolveDependency(requirement, _)
- pkg = parentPackage.findLocalPackage(packageName, _)
+ resolveDependency: (requirement, _) ->
+ # TODO: tolerate missing packages
+ pkg = requirement.requiringPackage.findLocalPackage(requirement.name, _)
if !pkg
- return @addError("Missing pkg '#{packageName}' required by pkg '#{parentPackage.name}' at #{parentPackage.dir}")
+ return @addError("Missing pkg '#{requirement.name}' required by pkg '#{requirement.requiringPackage.name}' at #{requirement.requiringPackage.dir}")
- pkg.setDestinationDependenciesDir(parentPackage.destinationDependenciesDir)
+ if not semver.satisfies(pkg.version, requirement.versionSpec)
+ @addError("Resolved package #{pkg.name}@#{pkg.version} does not satisfy range '#{requirement.versionSpec}' required by pkg '#{requirement.requiringPackage.name}' at #{requirement.requiringPackage.dir}")
+ requirement.resolvedPackage = pkg
@addPackage(pkg, _)
+
addError: (message) ->
@errors.push message
return undefined
- processEnqueuedPackages: (_) ->
- while pkg = @packagesToProcess.shift()
- @processPackage(pkg, _)
- @packagesToProcess = []
+
+ installPackage: (rootPackage, rootDestinationDir, _) ->
+ orderedSources = []
+
+ rootPackage.setDestinationDir(rootDestinationDir)
+ orderedSources.push(rootPackage)
+
+ for family in @selectedFamilies
+ console.log "#{family.name}"
+ for requirement in family.requirements
+ console.log " version #{requirement.versionSpec}"
+ for sourcePackage in family.localSources
+ console.log " source version #{sourcePackage.manifest.version} at #{sourcePackage.dir}"
+ # console.log "RUN: #{fill 35, name} #{fill 60, pkg.destinationDir} <- #{fill 90, pkg.dir}".trim()
+
+ resolvedSources = []
+
+ # pick source to install
+ if family == rootPackage.family
+ primarySource = rootPackage
+ primarySourceDir = rootDestinationDir
+ else
+ primarySourceDir = rootPackage.localDependenciesDir
+
+ if family.localSources.length > 0
+ for sourcePackage in family.localSources
+ rank = 0
+ for requirement in family.requirements
+ if semver.satisfies(sourcePackage.version, requirement.versionSpec)
+ rank += 1
+ sourcePackage.rank = rank
+
+ primarySource = U.max(family.localSources, (p) => p.rank)
+ primarySource.setDestinationParentDir(primarySourceDir)
+ resolvedSources.push(primarySource)
+ orderedSources.push(primarySource)
+
+ secondarySources = []
+ for requirement in family.requirements
+ if not semver.satisfies(primarySource.version, requirement.versionSpec)
+ source = requirement.resolvedPackage
+ if not semver.eq(primarySource.version, source.version)
+ source.destinationDir = null
+ source.secondarySourceRequirement = requirement
+ resolvedSources.push(source)
+ secondarySources.push(source)
+
+ # set destination dirs iteratively, because inner package dirs depend on outer package dirs
+ progressMade = yes
+ while progressMade
+ progressMade = no
+ for source in secondarySources when !source.destinationDir
+ if dir = source.secondarySourceRequirement.requiringPackage.localDependenciesDir
+ source.setDestinationParentDir(dir)
+ orderedSources.push(source)
+ progressMade = yes
+
+ family.resolvedSources = resolvedSources
+
+ for source in family.resolvedSources
+ console.log " resolved source #{source.manifest?.version} at #{source.dir}"
+
+
+ console.log "Installation plan:"
+ for source in orderedSources
+ name = "#{source.manifest.name}@#{source.manifest.version}:"
+ console.log " " + "#{fill 35, name} #{fill 60, source.destinationDir} <- #{fill 90, source.dir}".trim()
+
+
+ console.log "Installation..."
+ for source in orderedSources
+ @processPackage(source, _)
+
return undefined
processPackage: (pkg, _) ->
return if pkg.processed
pkg.processed = yes
name = "#{pkg.manifest.name}@#{pkg.manifest.version}:"
- console.log "RUN: #{fill 35, name} #{fill 60, pkg.destinationDir} <- #{fill 90, pkg.dir}".trim()
+ console.log " " + "#{fill 35, name} #{fill 60, pkg.destinationDir} <- #{fill 90, pkg.dir}".trim()
- if no
+ if yes
# rm pkgname-ver.tgz
try fs.unlink(pkg.tgzPath, _) catch e then null
@@ -186,9 +293,11 @@ class Packager
throw expectedError(EXITCODE_EXEC_ERROR, "Failed to npm pack #{pkg.dir}")
@untar pkg.tgzPath, pkg.destinationDir, _
+ try fs.unlink(pkg.tgzPath, _) catch e then null
+
untar: (sourceFile, destinationDir, _) ->
- mkdirp(destinationDir, 0o600, _)
+ mkdirp(destinationDir, _)
read = fs.createReadStream(sourceFile)
extract = zlib.createGunzip()
@@ -216,14 +325,19 @@ run = (options, _) ->
console.log "tar: %s", options.tar
packager = new Packager(options )
- packager.addPackageFolder(options.sourceDir, options.destinationDir, _)
- packager.processEnqueuedPackages(_)
+
+ pkg = packager.obtainPackage(options.sourceDir, _)
+ if !pkg
+ throw expectedError(EXITCODE_INVALID_CMDLINE, "Missing source folder #{sourceDir}")
+ packager.addPackage(pkg, _)
+
+ packager.installPackage(pkg, options.destinationDir, _)
if packager.errors.length > 0
console.error "Errors:"
for message in packager.errors
console.error "#{message}"
- throw new Error("failed")
+ throw expectedError(EXITCODE_ENV_ERROR, "failed")
undefined
Oops, something went wrong.

0 comments on commit bbfde45

Please sign in to comment.