#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" Module for analyzing and compiling IronPython scripts.
"""
import sys
import os
import modulefinder
import tempfile
import subprocess
import glob
import shutil
import warnings
# Original modules
from . import detect
from . import constants
from . import exceptions
[ドキュメント]class ModuleCompiler:
"""This class finds the modules required by your script and create a .NET assembly.
By default this class searches for pure-Python modules in the
IronPython standard library and the CPython site-packages directory.
:param list paths_to_scripts: Specify the paths to your scripts.
In creating a .EXE file, the first
element of this list must be the
path to the main file of your project.
:param str ipy_dir: Specify the IronPython directory, or it will be
automatically detected using :func:`ironpycompiler.detect.auto_detect`.
:param str pyc_path: (optional) Specify the path to pyc.py.
.. versionchanged:: 0.10.0
The argument ``pyc_path`` was added.
"""
def __init__(self, paths_to_scripts, ipy_dir = None, pyc_path = None):
""" Initialization.
"""
if ipy_dir is None:
self.ipy_dir = detect.auto_detect()[1]
else:
self.ipy_dir = ipy_dir
if pyc_path is None:
self.pyc_abspath = os.path.join(self.ipy_dir,
"Tools", "Scripts", "pyc.py")
else:
self.pyc_abspath = os.path.abspath(pyc_path)
self.paths_to_scripts = [os.path.abspath(x) for x in
paths_to_scripts] # コンパイルすべきスクリプトたち
self.dirs_of_modules = None # 依存モジュールたちのディレクトリ
#: Set of the names of built-in modules.
self.builtin_modules = set()
#: Set of the paths to required and compilable modules.
self.compilable_modules = set()
#: Set of the names of required but uncompilable modules.
self.uncompilable_modules = set()
self.response_file = None # pyc.pyに渡すレスポンスファイル
#: Standard output from pyc.py.
self.pyc_stdout = None
self.pyc_stderr = None # pyc.pyから得た標準エラー出力
#: The path to the main output assembly.
self.output_asm = None
[ドキュメント] def check_compilability(self, dirs_of_modules = None):
"""Check the compilability of the modules required by the scripts you specified.
:param list dirs_of_modules: Specify the paths of the
directories where the modules your
scripts require exist, or this
method searches for pure-Python
modules in the IronPython standard
library, and the CPython site-packages
directory.
"""
self.dirs_of_modules = dirs_of_modules
if self.dirs_of_modules is None:
self.dirs_of_modules = [os.path.join(self.ipy_dir,
"Lib")]
self.dirs_of_modules += [p for p in sys.path if
"site-packages" in p]
# 各スクリプトが依存するモジュールを探索する
for script in self.paths_to_scripts:
mf = modulefinder.ModuleFinder(path = self.dirs_of_modules)
mf.run_script(script)
self.uncompilable_modules |= set(mf.badmodules.keys())
for name, module in mf.modules.iteritems():
path_to_module = module.__file__
if path_to_module is None:
self.builtin_modules.add(name)
continue
elif os.path.splitext(path_to_module)[1] == ".pyd":
self.uncompilable_modules.add(name)
continue
else:
self.compilable_modules.add(
os.path.abspath(path_to_module))
self.compilable_modules -= set(self.paths_to_scripts)
[ドキュメント] def call_pyc(self, args, delete_resp = True,
executable = constants.EXECUTABLE, cwd = None):
"""Call pyc.py in order to compile your scripts.
In general use this method is not supposed to be called
directly. It is recommended that you use
:meth:`create_asm` instead.
:param list args: Specify the arguments that should be sent to
pyc.py.
:param bool delete_resp: (optional) Specify whether to delete the
response file after compilation or not.
:param str executable: (optional) Specify the name of the
Ironpython exectuable.
:param str cwd: (optional) Specify the current working directory.
"""
if cwd is None:
cwd = os.getcwd()
# レスポンスファイルを作る
self.response_file = tempfile.mkstemp(suffix = ".txt",
text = True, prefix = "IPC")
# レスポンスファイルに書き込む
for line in args:
os.write(self.response_file[0], line + "\n")
# レスポンスファイルを閉じる
os.close(self.response_file[0])
# pyc.pyを実行する
ipy_args = [os.path.splitext(executable)[0], self.pyc_abspath,
"@" + self.response_file[1]]
ipy_exe = os.path.abspath(os.path.join(self.ipy_dir,
executable))
sp = subprocess.Popen(args = ipy_args, executable = ipy_exe,
stdin = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.STDOUT, cwd = cwd)
(self.pyc_stdout, self.pyc_stderr) = sp.communicate()
#sp.terminate()
# ipyのエラーを確認する
if sp.returncode <> 0:
raise exceptions.ModuleCompilationError(
msg = "{0} returned {1} exit status.".format(executable, sp.returncode))
# レスポンスファイルを削除する
if delete_resp:
os.remove(self.response_file[1])
[ドキュメント] def create_dll(self, out = None, delete_resp = True,
executable = constants.EXECUTABLE):
"""Compile your scripts into a DLL file (.NET library assembly) using pyc.py.
:param str out: (optional) Specify the name of the DLL file
that should be created.
:param bool delete_resp: (optional) Specify whether to delete the
response file after compilation or not.
:param str executable: (optional) Specify the name of the
Ironpython exectuable.
.. warning::
This method is deprecated, and will be removed in the next
major version. Please use :meth:`create_asm`
instead.
"""
warnings.warn("Use create_asm instead.", DeprecationWarning)
if self.compilable_modules == set():
self.check_compilability()
# pycに送る引数
pyc_args = ["/target:dll"]
if out is not None:
out = os.path.abspath(out)
pyc_args.append(
"/out:" + os.path.splitext(os.path.basename(out))[0])
pyc_args += self.paths_to_scripts
pyc_args += self.compilable_modules
# call_pycに送る引数
call_args = {"args": pyc_args, "delete_resp": delete_resp,
"executable": executable}
if out is not None:
call_args["cwd"] = os.path.dirname(out)
self.call_pyc(**call_args)
[ドキュメント] def create_executable(self, out = None, winexe = False,
target_platform = None, embed = True, standalone = True,
mta = False, delete_resp = True, executable = constants.EXECUTABLE):
"""Compile your scripts into an EXE file (.NET process assembly) using pyc.py.
:param str out: (optional) Specify the name of the EXE file
that should be created.
:param bool winexe: (optional) Specify whether to create
a windows executable or to generate a
console one, or a console executable will be
created.
:param str target_platform: (optional) Specify the target
platform ("x86" or "x64") if
necessary.
:param bool embed: (optional) Specify whether to embed the
generated DLL into the executable.
:param bool standalone: (optional) Specify whether to embed
IronPython assemblies into the executable.
:param bool mta: (optional) Specify whether to set
MTAThreadAttribute (winexe).
:param bool delete_resp: (optional) Specify whether to delete the
response file after compilation or not.
:param str executable: (optional) Specify the name of the
Ironpython exectuable.
.. warning::
This method is deprecated, and will be removed in the next
major version. Please use :meth:`create_asm` instead.
"""
warnings.warn("Use create_asm instead.", DeprecationWarning)
if self.compilable_modules == set():
self.check_compilability()
# pyc.pyに送る引数
pyc_args = ["/main:" + self.paths_to_scripts[0]]
if out is not None:
out = os.path.abspath(out)
pyc_args.append(
"/out:" + os.path.splitext(os.path.basename(out))[0])
if winexe:
pyc_args.append("/target:winexe")
if mta:
pyc_args.append("/mta")
else:
pyc_args.append("/target:exe")
if target_platform in ["x86", "x64"]:
pyc_args.append("/platform:" + target_platform)
if embed:
pyc_args.append("/embed")
if standalone:
pyc_args.append("/standalone")
pyc_args += self.paths_to_scripts
pyc_args += self.compilable_modules
# call_pycに送る引数
call_args = {"args": pyc_args, "delete_resp": delete_resp,
"executable": executable}
if out is not None:
call_args["cwd"] = os.path.dirname(out)
self.call_pyc(**call_args)
[ドキュメント] def create_asm(self, out = None, target_asm = "dll",
target_platform = None, embed = True, standalone = True,
mta = False, delete_resp = True, executable = constants.EXECUTABLE,
copy_ipydll = False):
"""Compile your scripts into a .NET assembly, using pyc.py.
:param str out: (optional) Specify the name of the EXE file
that should be created, or the name of the main
script will be used and the destination
directory will be the current directory.
:param str target_asm: (optional) The type of the output assembly,
can be "dll", "exe", or "winexe".
By default a .DLL file will be created.
:param str target_platform: (optional) Specify the target
platform ("x86" or "x64") if
necessary (exe/winexe).
:param bool embed: (optional) Specify whether to embed the
generated DLL into the executable (exe/winexe).
:param bool standalone: (optional) Specify whether to embed
IronPython assemblies into the executable
(exe/winexe).
:param bool mta: (optional) Specify whether to set
MTAThreadAttribute (winexe).
:param bool delete_resp: (optional) Specify whether to delete the
response file after compilation or not.
:param str executable: (optional) Specify the name of the
Ironpython exectuable.
:param bool copy_ipydll: (optional) Specify whether to copy the
IronPython DLL files into the
destination directory.
"""
if self.compilable_modules == set():
self.check_compilability()
if out is None:
output_basename = os.path.splitext(os.path.basename(
self.paths_to_scripts[0]))[0]
if target_asm in ["exe", "winexe"]:
output_basename += ".exe"
else:
output_basename += ".dll"
self.output_asm = os.path.join(os.getcwd(), output_basename)
else:
self.output_asm = os.path.abspath(out)
pyc_args = ["/out:" + os.path.splitext(self.output_asm)[0]]
if target_asm in ["exe", "winexe"]:
pyc_args.append("/target:" + target_asm)
pyc_args.append("/main:" + self.paths_to_scripts[0])
if target_platform in ["x86", "x64"]:
pyc_args.append("/platform:" + target_platform)
if embed:
pyc_args.append("/embed")
if standalone:
pyc_args.append("/standalone")
if target_asm == "winexe" and mta:
pyc_args.append("/mta")
pyc_args += self.paths_to_scripts
pyc_args += self.compilable_modules
call_args = {"args": pyc_args, "delete_resp": delete_resp,
"executable": executable, "cwd": os.path.dirname(self.output_asm)}
self.call_pyc(**call_args)
if copy_ipydll:
gather_ipydll(dest_dir = os.path.dirname(self.output_asm),
ipy_dir = self.ipy_dir)
[ドキュメント]def gather_ipydll(dest_dir, ipy_dir = None):
""" Copy the IronPython DLL files into the directory specified.
:param str dest_dir: The path of the destination directory.
:param str ipy_dir: Specify the path of the IronPython directory, or
it will be detected using :func:`ironpycompiler.detect.auto_detect`.
.. versionadded:: 0.9.0
.. versionchanged:: 0.10.0
This function now uses :func:`ironpycompiler.detect.auto_detect` instead of
:func:`ironpycompiler.detect.detect_ipy`.
"""
if ipy_dir is None:
ipy_dir = detect.auto_detect()[1]
for dll in glob.glob(os.path.join(ipy_dir, "*.dll")):
shutil.copy2(dll, dest_dir)