text: #####################################################################################
  #
  #  Copyright (c) Microsoft Corporation. All rights reserved.
  #
  # This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  # copy of the license can be found in the License.html file at the root of this distribution. If
  # you cannot locate the  Apache License, Version 2.0, please send an email to
  # ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  # by the terms of the Apache License, Version 2.0.
  #
  # You must not remove this notice, or any other, from this software.
  #
  #
  #####################################################################################
  
  """
  pyc: The Command-Line Python Compiler
  
  Usage: ipy.exe pyc.py [options] file [file ...]
  
  Options:
      /out:output_file                          Output file name (default is main_file.<extenstion>)
      /target:dll                               Compile only into dll.  Default
      /target:exe                               Generate console executable stub for startup in addition to dll.
      /target:winexe                            Generate windows executable stub for startup in addition to dll.
      @<file>                                   Specifies a response file to be parsed for input files and command line options (one per line)
      /file_version:<version>                   Set the file/assembly version
      /? /h                                     This message    
  
  EXE/WinEXE specific options:
      /main:main_file.py                        Main file of the project (module to be executed first)
      /platform:x86                             Compile for x86 only
      /platform:x64                             Compile for x64 only
      /embed                                    Embeds the generated DLL as a resource into the executable which is loaded at runtime
      /standalone                               Embeds the IronPython assemblies into the stub executable.
      /mta                                      Set MTAThreadAttribute on Main instead of STAThreadAttribute, only valid for /target:winexe
      /file_info_product:<name>                 Set product name in executable meta information
      /file_info_product_version:<version>      Set product version in executable meta information
      /file_info_company:<name>                 Set company name in executable meta information
      /file_info_copyright:<info>               Set copyright information in executable meta information
      /file_info_trademark:<info>               Set trademark information in executable meta information
  
  Example:
      ipy.exe pyc.py /main:Program.py Form.py /target:winexe
  """
  
  import sys
  import clr
  clr.AddReferenceByPartialName("IronPython")
  
  from System.Collections.Generic import List
  import IronPython.Hosting as Hosting
  from IronPython.Runtime.Operations import PythonOps
  import System
  from System import Version
  from System.Reflection import Emit, Assembly
  from System.Reflection.Emit import OpCodes, AssemblyBuilderAccess
  from System.Reflection import AssemblyName, TypeAttributes, MethodAttributes, ResourceAttributes, CallingConventions
  
  def GenerateExe(config):
      """generates the stub .EXE file for starting the app"""
      aName = AssemblyName(System.IO.FileInfo(config.output).Name)
      if config.file_version is not None:
          aName.Version = Version(config.file_version)
      
      ab = PythonOps.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave)
      ab.DefineVersionInfoResource(config.file_info_product, config.file_info_product_version, config.file_info_company, config.file_info_company, config.file_info_trademark)
      mb = ab.DefineDynamicModule(config.output,  aName.Name + ".exe")
      tb = mb.DefineType("PythonMain", TypeAttributes.Public)
      assemblyResolveMethod = None
  
      if config.standalone:
          print "Generating stand alone executable"
          config.embed = True
          
          for a in System.AppDomain.CurrentDomain.GetAssemblies():
              n = AssemblyName(a.FullName)
              if not a.IsDynamic and not a.EntryPoint and (n.Name.StartsWith("IronPython") or n.Name in ['Microsoft.Dynamic', 'Microsoft.Scripting']):                
                  print "\tEmbedding %s %s" % (n.Name, str(n.Version))
                  f = System.IO.FileStream(a.Location, System.IO.FileMode.Open, System.IO.FileAccess.Read)
                  mb.DefineManifestResource("Dll." + n.Name, f, ResourceAttributes.Public)
  
          # we currently do no error checking on what is passed in to the assemblyresolve event handler
          assemblyResolveMethod = tb.DefineMethod("AssemblyResolve", MethodAttributes.Public | MethodAttributes.Static, clr.GetClrType(Assembly), (clr.GetClrType(System.Object), clr.GetClrType(System.ResolveEventArgs)))
          gen = assemblyResolveMethod.GetILGenerator()
          s = gen.DeclareLocal(clr.GetClrType(System.IO.Stream)) # resource stream
          gen.Emit(OpCodes.Ldnull)
          gen.Emit(OpCodes.Stloc, s)
          d = gen.DeclareLocal(clr.GetClrType(System.Array[System.Byte])) # data buffer
          gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("GetEntryAssembly"), ())
          gen.Emit(OpCodes.Ldstr, "Dll.")
          gen.Emit(OpCodes.Ldarg_1)    # The event args
          gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.ResolveEventArgs).GetMethod("get_Name"), ())
          gen.Emit(OpCodes.Newobj, clr.GetClrType(AssemblyName).GetConstructor((str, )))
          gen.EmitCall(OpCodes.Call, clr.GetClrType(AssemblyName).GetMethod("get_Name"), ())
          gen.EmitCall(OpCodes.Call, clr.GetClrType(str).GetMethod("Concat", (str, str)), ())
          gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(Assembly).GetMethod("GetManifestResourceStream", (str, )), ())
          gen.Emit(OpCodes.Stloc, s)
          gen.Emit(OpCodes.Ldloc, s)
          gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.IO.Stream).GetMethod("get_Length"), ())
          gen.Emit(OpCodes.Newarr, clr.GetClrType(System.Byte))
          gen.Emit(OpCodes.Stloc, d)
          gen.Emit(OpCodes.Ldloc, s)
          gen.Emit(OpCodes.Ldloc, d)
          gen.Emit(OpCodes.Ldc_I4_0)
          gen.Emit(OpCodes.Ldloc, s)
          gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.IO.Stream).GetMethod("get_Length"), ())
          gen.Emit(OpCodes.Conv_I4)
          gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.IO.Stream).GetMethod("Read", (clr.GetClrType(System.Array[System.Byte]), int, int)), ())
          gen.Emit(OpCodes.Pop)
          gen.Emit(OpCodes.Ldloc, d)
          gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("Load", (clr.GetClrType(System.Array[System.Byte]), )), ())
          gen.Emit(OpCodes.Ret)
  
          # generate a static constructor to assign the AssemblyResolve handler (otherwise it tries to use IronPython before it adds the handler)
          # the other way of handling this would be to move the call to InitializeModule into a separate method.
          staticConstructor = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, System.Type.EmptyTypes)
          gen = staticConstructor.GetILGenerator()
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.AppDomain).GetMethod("get_CurrentDomain"), ())
          gen.Emit(OpCodes.Ldnull)
          gen.Emit(OpCodes.Ldftn, assemblyResolveMethod)
          gen.Emit(OpCodes.Newobj, clr.GetClrType(System.ResolveEventHandler).GetConstructor((clr.GetClrType(System.Object), clr.GetClrType(System.IntPtr))))
          gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(System.AppDomain).GetMethod("add_AssemblyResolve"), ())
          gen.Emit(OpCodes.Ret)        
  
      mainMethod = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, int, ())
      if config.target == System.Reflection.Emit.PEFileKinds.WindowApplication and config.mta:
          mainMethod.SetCustomAttribute(clr.GetClrType(System.MTAThreadAttribute).GetConstructor(()), System.Array[System.Byte](()))
      elif config.target == System.Reflection.Emit.PEFileKinds.WindowApplication:
          mainMethod.SetCustomAttribute(clr.GetClrType(System.STAThreadAttribute).GetConstructor(()), System.Array[System.Byte](()))
  
      gen = mainMethod.GetILGenerator()
  
      # get the ScriptCode assembly...
      if config.embed:
          # put the generated DLL into the resources for the stub exe
          w = mb.DefineResource("IPDll.resources", "Embedded IronPython Generated DLL")
          w.AddResource("IPDll." + config.output, System.IO.File.ReadAllBytes(config.output + ".dll"))
          System.IO.File.Delete(config.output + ".dll")
  
          # generate code to load the resource
          gen.Emit(OpCodes.Ldstr, "IPDll")
          gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("GetEntryAssembly"), ())
          gen.Emit(OpCodes.Newobj, clr.GetClrType(System.Resources.ResourceManager).GetConstructor((str, clr.GetClrType(Assembly))))
          gen.Emit(OpCodes.Ldstr, "IPDll." + config.output)
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Resources.ResourceManager).GetMethod("GetObject", (str, )), ())
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Reflection.Assembly).GetMethod("Load", (clr.GetClrType(System.Array[System.Byte]), )), ())
      else:
          # variables for saving original working directory und return code of script
          wdSave = gen.DeclareLocal(str)
  
          # save current working directory
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Environment).GetMethod("get_CurrentDirectory"), ())
          gen.Emit(OpCodes.Stloc, wdSave)
          gen.EmitCall(OpCodes.Call, clr.GetClrType(Assembly).GetMethod("GetEntryAssembly"), ())
          gen.EmitCall(OpCodes.Callvirt, clr.GetClrType(Assembly).GetMethod("get_Location"), ())
          gen.Emit(OpCodes.Newobj, clr.GetClrType(System.IO.FileInfo).GetConstructor( (str, ) ))
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.IO.FileInfo).GetMethod("get_Directory"), ())
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.IO.DirectoryInfo).GetMethod("get_FullName"), ())
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Environment).GetMethod("set_CurrentDirectory"), ())
          gen.Emit(OpCodes.Ldstr, config.output + ".dll")
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.IO.Path).GetMethod("GetFullPath", (clr.GetClrType(str), )), ())
          # result of GetFullPath stays on the stack during the restore of the
          # original working directory
  
          # restore original working directory
          gen.Emit(OpCodes.Ldloc, wdSave)
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Environment).GetMethod("set_CurrentDirectory"), ())
  
          # for the LoadFile() call, the full path of the assembly is still is on the stack
          # as the result from the call to GetFullPath()
          gen.EmitCall(OpCodes.Call, clr.GetClrType(System.Reflection.Assembly).GetMethod("LoadFile", (clr.GetClrType(str), )), ())
  
      # emit module name
      gen.Emit(OpCodes.Ldstr, "__main__")  # main module name
      gen.Emit(OpCodes.Ldnull)             # no references
      gen.Emit(OpCodes.Ldc_I4_0)           # don't ignore environment variables for engine startup
  
      # call InitializeModule
      # (this will also run the script)
      ctype=clr.GetClrType(PythonOps)
      print(dir(ctype))
      ops=ctype.GetMethod("InitializeModuleEx")
      gen.EmitCall(OpCodes.Call,ops, ())
      gen.Emit(OpCodes.Ret)
      tb.CreateType()
      ab.SetEntryPoint(mainMethod, config.target)
      ab.Save(aName.Name + ".exe", config.platform, config.machine)
  
  class Config(object):
      def __init__(self):
          self.output = None
          self.main = None
          self.main_name = None
          self.target = System.Reflection.Emit.PEFileKinds.Dll
          self.embed = False
          self.standalone = False
          self.mta = False
          self.platform = System.Reflection.PortableExecutableKinds.ILOnly
          self.machine = System.Reflection.ImageFileMachine.I386
          self.files = []
          self.file_info_product = None
          self.file_info_product_version = None
          self.file_info_company = None
          self.file_info_copyright = None
          self.file_info_trademark = None
          self.file_version = None
  
      def ParseArgs(self, args, respFiles=[]):
          for arg in args:
              arg = arg.strip()
              if arg.startswith("#"):
                  continue
  
              if arg.startswith("/main:"):
                  self.main_name = self.main = arg[6:]
                  # only override the target kind if its current a DLL
                  if self.target == System.Reflection.Emit.PEFileKinds.Dll:
                      self.target = System.Reflection.Emit.PEFileKinds.ConsoleApplication
  
              elif arg.startswith("/out:"):
                  self.output = arg[5:]
  
              elif arg.startswith("/target:"):
                  tgt = arg[8:]
                  if tgt == "exe": self.target = System.Reflection.Emit.PEFileKinds.ConsoleApplication
                  elif tgt == "winexe": self.target = System.Reflection.Emit.PEFileKinds.WindowApplication
                  else: self.target = System.Reflection.Emit.PEFileKinds.Dll
  
              elif arg.startswith("/platform:"):
                  pform = arg[10:]
                  if pform == "x86":
                      self.platform = System.Reflection.PortableExecutableKinds.ILOnly | System.Reflection.PortableExecutableKinds.Required32Bit
                      self.machine  = System.Reflection.ImageFileMachine.I386
                  elif pform == "x64":
                      self.platform = System.Reflection.PortableExecutableKinds.ILOnly | System.Reflection.PortableExecutableKinds.PE32Plus
                      self.machine  = System.Reflection.ImageFileMachine.AMD64
                  else:
                      self.platform = System.Reflection.PortableExecutableKinds.ILOnly
                      self.machine  = System.Reflection.ImageFileMachine.I386
  
              elif arg.startswith("/file_info_product:"):
                  self.file_info_product = arg[19:]
              elif arg.startswith("/file_info_product_version:"):
                  self.file_info_product_version = arg[27:]
              elif arg.startswith("/file_info_company:"):
                  self.file_info_company = arg[19:]
              elif arg.startswith("/file_info_copyright:"):
                  self.file_info_copyright = arg[21:]
              elif arg.startswith("/file_info_trademark:"):
                  self.file_info_trademark = arg[21:]
  
              elif arg.startswith("/file_version:"):
                  self.file_version = arg[14:]
  
              elif arg.startswith("/embed"):
                  self.embed = True
  
              elif arg.startswith("/standalone"):
                  self.standalone = True
  
              elif arg.startswith("/mta"):
                  self.mta = True
  
              elif arg in ["/?", "-?", "/h", "-h"]:
                  print __doc__
                  sys.exit(0)
  
              else:
                  if arg.startswith("@"):
                      respFile = System.IO.Path.GetFullPath(arg[1:])
                      if not respFile in respFiles:
                          respFiles.append(respFile)
                          with open(respFile, 'r') as f:
                             self.ParseArgs(f.readlines(), respFiles)
                      else:
                          print "WARNING: Already parsed response file '%s'\n" % arg[1:]
                  else:
                      self.files.append(arg)
  
      def Validate(self):
          if not self.files and not self.main_name:
              print "No files or main defined"
              return False
  
          if self.target != System.Reflection.Emit.PEFileKinds.Dll and self.main_name == None:
              print "EXEs require /main:<filename> to be specified"
              return False
  
          if not self.output and self.main_name:
              self.output = System.IO.Path.GetFileNameWithoutExtension(self.main_name)
          elif not self.output and self.files:
              self.output = System.IO.Path.GetFileNameWithoutExtension(self.files[0])
  
          return True
  
      def __repr__(self):
          res = "Input Files:\n"
          for file in self.files:
              res += "\t%s\n" % file
  
          res += "Output:\n\t%s\n" % self.output
          res += "Target:\n\t%s\n" % self.target
          res += "Platform:\n\t%s\n" % self.platform
          res += "Machine:\n\t%s\n" % self.machine
  
          if self.target == System.Reflection.Emit.PEFileKinds.WindowApplication:
              res += "Threading:\n"
              if self.mta:
                  res += "\tMTA\n"
              else:
                  res += "\tSTA\n"        
          return res
  
  def Main(args):
      files = []
      config = Config()
      
      config.ParseArgs(args)
      if not config.Validate():
          print __doc__
          sys.exit(0)
  
      print config
      
      print "Compiling..."
      print config
      print config.files
      print config.main_name
      print config.output
      clr.CompileModules(config.output + ".dll", mainModule = config.main_name, *config.files)
  
      if config.target != System.Reflection.Emit.PEFileKinds.Dll:
          GenerateExe(config)
  
      print "Saved to %s" % (config.output, )
  
  if __name__ == "__main__":
      Main(sys.argv[1:])
  