Browse Source

Merge pull request #81 from atom/window-native-modules

Fix node native modules support on Windows
Cheng Zhao 11 years ago
parent
commit
504f96ae08
7 changed files with 188 additions and 53 deletions
  1. 1 1
      app/atom_main.cc
  2. 33 0
      atom.gyp
  3. 8 2
      browser/atom/atom.coffee
  4. 5 1
      script/build.py
  5. 67 18
      script/create-dist.py
  6. 10 0
      script/lib/util.py
  7. 64 31
      script/upload.py

+ 1 - 1
app/atom_main.cc

@@ -21,7 +21,7 @@ int Start(int argc, char *argv[]);
 
 int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
   int argc = 0;
-  wchar_t** wargv = ::CommandLineToArgvW(cmd, &argc);
+  wchar_t** wargv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
   if (argc > 1 && wcscmp(wargv[1], L"--atom-child_process-fork") == 0) {
     // Convert argv to to UTF8
     char** argv = new char*[argc];

+ 33 - 0
atom.gyp

@@ -275,6 +275,7 @@
               'destination': '<(PRODUCT_DIR)',
               'files': [
                 '<(libchromiumcontent_library_dir)/chromiumcontent.dll',
+                '<(libchromiumcontent_library_dir)/ffmpegsumo.dll',
                 '<(libchromiumcontent_library_dir)/icudt.dll',
                 '<(libchromiumcontent_library_dir)/libGLESv2.dll',
                 '<(libchromiumcontent_resources_dir)/content_shell.pak',
@@ -469,5 +470,37 @@
         },  # target helper
       ],
     }],  # OS==Mac
+    ['OS=="win"', {
+      'targets': [
+        {
+          'target_name': 'generate_node_lib',
+          'type': 'none',
+          'dependencies': [
+            '<(project_name)',
+          ],
+          'actions': [
+            {
+              'action_name': 'Create node.lib',
+              'inputs': [
+                '<(PRODUCT_DIR)/atom.lib',
+                '<(libchromiumcontent_library_dir)/chromiumcontent.dll.lib',
+              ],
+              'outputs': [
+                '<(PRODUCT_DIR)/node.lib',
+              ],
+              'action': [
+                'lib.exe',
+                '/nologo',
+                # We can't use <(_outputs) here because that escapes the
+                # backslash in the path, which confuses lib.exe.
+                '/OUT:<(PRODUCT_DIR)\\node.lib',
+                '<@(_inputs)',
+              ],
+              'msvs_cygwin_shell': 0,
+            },
+          ],
+        },  # target generate_node_lib
+      ],
+    }],  # OS==win
   ],
 }

+ 8 - 2
browser/atom/atom.coffee

@@ -1,12 +1,18 @@
 fs = require 'fs'
 path = require 'path'
 
-# Redirect node's console to use our own implementations, since node can not
-# handle output when running as GUI program.
 if process.platform is 'win32'
+  # Redirect node's console to use our own implementations, since node can not
+  # handle output when running as GUI program.
   console.log = console.error = console.warn = process.log
   process.stdout.write = process.stderr.write = process.log
 
+  # Always returns EOF for stdin stream.
+  Readable = require('stream').Readable
+  stdin = new Readable
+  stdin.push null
+  process.__defineGetter__ 'stdin', -> stdin
+
 # Provide default Content API implementations.
 atom = {}
 

+ 5 - 1
script/build.py

@@ -20,7 +20,7 @@ def main():
   args = parse_args()
   for config in args.configuration:
     build_path = os.path.join('out', config)
-    subprocess.call([ninja, '-C', build_path])
+    subprocess.call([ninja, '-C', build_path, args.target])
 
 
 def parse_args():
@@ -30,6 +30,10 @@ def parse_args():
                       nargs='+',
                       default=CONFIGURATIONS,
                       required=False)
+  parser.add_argument('-t', '--target',
+                      help='Build specified target',
+                      default='atom',
+                      required=False)
   return parser.parse_args()
 
 

+ 67 - 18
script/create-dist.py

@@ -11,14 +11,42 @@ import tarfile
 from lib.util import *
 
 
+ATOM_SHELL_VRESION = get_atom_shell_version()
+NODE_VERSION = 'v0.10.15'
+
 SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
 DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
 NODE_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'node')
-DIST_HEADERS_NAME = 'node-{0}'.format(get_atom_shell_version())
+DIST_HEADERS_NAME = 'node-{0}'.format(NODE_VERSION)
 DIST_HEADERS_DIR = os.path.join(DIST_DIR, DIST_HEADERS_NAME)
 
-BUNDLE_NAME = 'Atom.app'
-BUNDLE_DIR = os.path.join(SOURCE_ROOT, 'out', 'Release', BUNDLE_NAME)
+TARGET_PLATFORM = {
+  'cygwin': 'win32',
+  'darwin': 'darwin',
+  'linux2': 'linux',
+  'win32': 'win32',
+}[sys.platform]
+
+TARGET_BINARIES = {
+  'darwin': [
+  ],
+  'win32': [
+    'atom.exe',
+    'chromiumcontent.dll',
+    'content_shell.pak',
+    'ffmpegsumo.dll',
+    'icudt.dll',
+    'libGLESv2.dll',
+  ],
+}
+TARGET_DIRECTORIES = {
+  'darwin': [
+    'Atom.app',
+  ],
+  'win32': [
+    'resources',
+  ],
+}
 
 HEADERS_SUFFIX = [
   '.h',
@@ -27,7 +55,6 @@ HEADERS_SUFFIX = [
 HEADERS_DIRS = [
   'src',
   'deps/http_parser',
-  'deps/v8',
   'deps/zlib',
   'deps/uv',
 ]
@@ -56,12 +83,20 @@ def force_build():
 
 
 def copy_binaries():
-  shutil.copytree(BUNDLE_DIR, os.path.join(DIST_DIR, BUNDLE_NAME),
-                  symlinks=True)
+  out_dir = os.path.join(SOURCE_ROOT, 'out', 'Release')
+
+  for binary in TARGET_BINARIES[TARGET_PLATFORM]:
+    shutil.copy2(os.path.join(out_dir, binary), DIST_DIR)
+
+  for directory in TARGET_DIRECTORIES[TARGET_PLATFORM]:
+    shutil.copytree(os.path.join(out_dir, directory),
+                    os.path.join(DIST_DIR, directory),
+                    symlinks=True)
 
 
 def copy_headers():
   os.mkdir(DIST_HEADERS_DIR)
+  # Copy standard node headers from node. repository.
   for include_path in HEADERS_DIRS:
     abs_path = os.path.join(NODE_DIR, include_path)
     for dirpath, dirnames, filenames in os.walk(abs_path):
@@ -71,7 +106,19 @@ def copy_headers():
           continue
         copy_source_file(os.path.join(dirpath, filename))
   for other_file in HEADERS_FILES:
-    copy_source_file(os.path.join(NODE_DIR, other_file))
+    copy_source_file(source = os.path.join(NODE_DIR, other_file))
+
+  # Copy V8 headers from chromium's repository.
+  src = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', 'download',
+                    'libchromiumcontent', 'src')
+  for dirpath, dirnames, filenames in os.walk(os.path.join(src, 'v8')):
+    for filename in filenames:
+      extension = os.path.splitext(filename)[1]
+      if extension not in HEADERS_SUFFIX:
+        continue
+      copy_source_file(source=os.path.join(dirpath, filename),
+                       start=src,
+                       destination=os.path.join(DIST_HEADERS_DIR, 'deps'))
 
 
 def copy_license():
@@ -82,17 +129,19 @@ def copy_license():
 def create_version():
   version_path = os.path.join(SOURCE_ROOT, 'dist', 'version')
   with open(version_path, 'w') as version_file:
-    version_file.write(get_atom_shell_version())
+    version_file.write(ATOM_SHELL_VRESION)
 
 
 def create_zip():
-  print "Zipping distribution..."
-  zip_file = os.path.join(SOURCE_ROOT, 'dist', 'atom-shell.zip')
-  safe_unlink(zip_file)
+  dist_name = 'atom-shell-{0}-{1}.zip'.format(ATOM_SHELL_VRESION,
+                                              TARGET_PLATFORM)
+  zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
 
   with scoped_cwd(DIST_DIR):
-    files = ['Atom.app', 'LICENSE', 'version']
-    subprocess.check_call(['zip', '-r', '-y', zip_file] + files)
+    files = TARGET_BINARIES[TARGET_PLATFORM] +  \
+            TARGET_DIRECTORIES[TARGET_PLATFORM] + \
+            ['LICENSE', 'version']
+    make_zip(zip_file, files)
 
 
 def create_header_tarball():
@@ -102,11 +151,11 @@ def create_header_tarball():
     tarball.close()
 
 
-def copy_source_file(source):
-  relative = os.path.relpath(source, start=NODE_DIR)
-  destination = os.path.join(DIST_HEADERS_DIR, relative)
-  safe_mkdir(os.path.dirname(destination))
-  shutil.copy2(source, destination)
+def copy_source_file(source, start=NODE_DIR, destination=DIST_HEADERS_DIR):
+  relative = os.path.relpath(source, start=start)
+  final_destination = os.path.join(destination, relative)
+  safe_mkdir(os.path.dirname(final_destination))
+  shutil.copy2(source, final_destination)
 
 
 if __name__ == '__main__':

+ 10 - 0
script/lib/util.py

@@ -64,6 +64,16 @@ def extract_zip(zip_path, destination):
     with zipfile.ZipFile(zip_path) as z:
       z.extractall(destination)
 
+def make_zip(zip_file_path, files):
+  safe_unlink(zip_file_path)
+  if sys.platform == 'darwin':
+    subprocess.check_call(['zip', '-r', '-y', zip_file_path] + files)
+  else:
+    zip_file = zipfile.ZipFile(zip_file_path, "w")
+    for filename in files:
+      zip_file.write(filename, filename)
+    zip_file.close()
+
 
 def rm_rf(path):
   try:

+ 64 - 31
script/upload.py

@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 
+import argparse
 import errno
 import glob
 import os
@@ -10,29 +11,41 @@ import tempfile
 from lib.util import *
 
 
+TARGET_PLATFORM = {
+  'cygwin': 'win32',
+  'darwin': 'darwin',
+  'linux2': 'linux',
+  'win32': 'win32',
+}[sys.platform]
+
+ATOM_SHELL_VRESION = get_atom_shell_version()
+NODE_VERSION = 'v0.10.15'
+
 SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'Release')
 DIST_DIR = os.path.join(SOURCE_ROOT, 'dist')
+DIST_NAME = 'atom-shell-{0}-{1}.zip'.format(ATOM_SHELL_VRESION, TARGET_PLATFORM)
 
 
 def main():
-  try:
-    ensure_s3put()
-    if not dist_newer_than_head():
-      create_dist = os.path.join(SOURCE_ROOT, 'script', 'create-dist.py')
-      subprocess.check_call([sys.executable, create_dist])
-    upload()
-  except AssertionError as e:
-    return e.message
-
-
-def ensure_s3put():
-  output = ''
-  try:
-    output = subprocess.check_output(['s3put', '--help'])
-  except OSError as e:
-    if e.errno != errno.ENOENT:
-      raise
-  assert 'multipart' in output, 'Error: Please install boto and filechunkio'
+  args = parse_args()
+
+  if not dist_newer_than_head():
+    create_dist = os.path.join(SOURCE_ROOT, 'script', 'create-dist.py')
+    subprocess.check_call([sys.executable, create_dist])
+
+  bucket, access_key, secret_key = s3_config()
+  upload(bucket, access_key, secret_key)
+  if not args.no_update_version:
+    update_version(bucket, access_key, secret_key)
+
+
+def parse_args():
+  parser = argparse.ArgumentParser(description='upload distribution file')
+  parser.add_argument('-n', '--no-update-version',
+                      help='Do not update the latest version file',
+                      action='store_false')
+  return parser.parse_args()
 
 
 def dist_newer_than_head():
@@ -40,7 +53,7 @@ def dist_newer_than_head():
     try:
       head_time = subprocess.check_output(['git', 'log', '--pretty=format:%at',
                                            '-n', '1']).strip()
-      dist_time = os.path.getmtime(os.path.join(DIST_DIR, 'atom-shell.zip'))
+      dist_time = os.path.getmtime(os.path.join(DIST_DIR, DIST_NAME))
     except OSError as e:
       if e.errno != errno.ENOENT:
         raise
@@ -49,17 +62,36 @@ def dist_newer_than_head():
   return dist_time > int(head_time)
 
 
-def upload():
+def upload(bucket, access_key, secret_key, version=ATOM_SHELL_VRESION):
   os.chdir(DIST_DIR)
-  bucket, access_key, secret_key = s3_config()
 
-  version = get_atom_shell_version()
   s3put(bucket, access_key, secret_key, DIST_DIR,
-        'atom-shell/{0}'.format(version), ['atom-shell.zip'])
+        'atom-shell/{0}'.format(version), [DIST_NAME])
   s3put(bucket, access_key, secret_key, DIST_DIR,
-        'atom-shell/dist/{0}'.format(version), glob.glob('node-*.tar.gz'))
+        'atom-shell/dist/{0}'.format(NODE_VERSION), glob.glob('node-*.tar.gz'))
+
+  if TARGET_PLATFORM == 'win32':
+    # Generate the node.lib.
+    build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
+    subprocess.check_call([sys.executable, build, '-c', 'Release',
+                          '-t', 'generate_node_lib'])
+
+    # Upload the 32bit node.lib.
+    node_lib = os.path.join(OUT_DIR, 'node.lib')
+    s3put(bucket, access_key, secret_key, OUT_DIR,
+          'atom-shell/dist/{0}'.format(NODE_VERSION), [node_lib])
+
+    # Upload the fake 64bit node.lib.
+    touch_x64_node_lib()
+    node_lib = os.path.join(OUT_DIR, 'x64', 'node.lib')
+    s3put(bucket, access_key, secret_key, OUT_DIR,
+          'atom-shell/dist/{0}'.format(NODE_VERSION), [node_lib])
 
-  update_version(bucket, access_key, secret_key)
+
+def update_version(bucket, access_key, secret_key):
+  prefix = os.path.join(SOURCE_ROOT, 'dist')
+  version = os.path.join(prefix, 'version')
+  s3put(bucket, access_key, secret_key, prefix, 'atom-shell', [version])
 
 
 def s3_config():
@@ -73,12 +105,6 @@ def s3_config():
   return config
 
 
-def update_version(bucket, access_key, secret_key):
-  prefix = os.path.join(SOURCE_ROOT, 'dist')
-  version = os.path.join(prefix, 'version')
-  s3put(bucket, access_key, secret_key, prefix, 'atom-shell', [ version ])
-
-
 def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
   args = [
     's3put',
@@ -93,6 +119,13 @@ def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
   subprocess.check_call(args)
 
 
+def touch_x64_node_lib():
+  x64_dir = os.path.join(OUT_DIR, 'x64')
+  safe_mkdir(x64_dir)
+  with open(os.path.join(x64_dir, 'node.lib'), 'w+') as node_lib:
+    node_lib.write('Invalid library')
+
+
 if __name__ == '__main__':
   import sys
   sys.exit(main())