Commit e4840f1f authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

New Q# build script leveraging dotnet MSBuild completely



i.e. quantum SDK detection, resolving Q# references, etc.

Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 156cca9c
Loading
Loading
Loading
Loading
+80 −3
Original line number Diff line number Diff line
#!/usr/bin/env python3
import argparse, sys, os, glob, subprocess, mimetypes, re, pathlib

# Helper to generate Q# build config by running the SDK
# Input: the source file directory where the build
# csproj will be saved to.
def generate_qsc_build_command(target_name, src_dir, input_files):
  import os, tempfile, shutil
  temp_dir = tempfile.mkdtemp()
  # print(temp_dir)
  os.system('dotnet new console -lang Q# -o ' + temp_dir + ' -n test')

  import xml.etree.ElementTree as ET
  # Parse the SDK generated Project file
  tree = ET.parse(temp_dir + '/test.csproj')

  # get root element
  root = tree.getroot()
  # run this target:
  root.attrib['DefaultTargets'] = 'PrepareQSharpCompile'
  propertyGroup = root.find('PropertyGroup')
  # enable QIR generation
  qir_gen = ET.Element('QirGeneration')
  qir_gen.text = 'true'
  propertyGroup.append(qir_gen)

  # Add a target to query the actual Build Command
  # to get the most accurate QSC executable:
  # <Target Name="PrintBuildCommand" BeforeTargets="PrepareQSharpCompile" >
  #   <WriteLinesToFile File="QscExe.cmd" Overwrite="true" Lines="$(QscExe)" />
  # </Target>

  export_qsc_cmd = ET.Element('WriteLinesToFile')
  export_qsc_cmd.attrib['File'] = 'QscExe.cmd'
  export_qsc_cmd.attrib['Overwrite'] = 'true'
  export_qsc_cmd.attrib['Lines'] = '$(QscExe)'

  export_qsc_cmd_target = ET.Element('Target')
  export_qsc_cmd_target.attrib['Name'] = 'PrintBuildCommand'
  export_qsc_cmd_target.attrib['BeforeTargets'] = 'PrepareQSharpCompile'
  export_qsc_cmd_target.append(export_qsc_cmd)

  root.append(export_qsc_cmd_target)
  tree.write(src_dir + '/output.csproj')
  # Delete the temp_directory:
  shutil.rmtree(temp_dir)
  os.system('dotnet build ' + src_dir + '/output.csproj')
  qsc_cmd = open(src_dir + '/QscExe.cmd', 'r').read()
  qsc_cmd = qsc_cmd.replace('\r', '').replace('\n', '')
  # Add the build verb:
  qsc_cmd += " build "
  # response file:
  qsc_rsp = open(src_dir + '/obj/qsharp/config/qsc.rsp', 'r').read()
  # parse arguments in the file
  import shlex
  split_args = shlex.split(qsc_rsp)
  skip_args = False
  for arg in split_args:
    if skip_args and arg.startswith('--'):
      skip_args = False
    if (arg not in ['--input', '--output']) and not skip_args:
      if arg.startswith('AssemblyName:'):
        qsc_cmd += (' AssemblyName:' + target_name)
      else:
        qsc_cmd += (' ' + arg + ' ')
    elif arg == '--input':
      qsc_cmd += ' --input'
      for input_file in input_files:
        qsc_cmd += (' ' + os.path.abspath(input_file))
      # Flag to ignore original file:
      skip_args = True
    elif arg == '--output':
      qsc_cmd += ' --output ' + src_dir
      # Flag to ignore original option:
      skip_args = True

  # print(qsc_cmd)
  return qsc_cmd

def main(argv=None):
    compiler = '@CLANG_EXECUTABLE@'
    verbose=False
@@ -361,9 +437,9 @@ def main(argv=None):
            os.remove(bc_file_name)

    if fileType == 'qsharp':
        # Assume that `qsc` is a valid exe command
        # i.e. need to set the PATH env appropriately.
        base_name = os.path.splitext(filename)[0]
        # Generate build command:
        build_cmd = generate_qsc_build_command(base_name, os.path.dirname(os.path.abspath(filename)), [filename, '@CMAKE_INSTALL_PREFIX@/include/qsharp/QirTarget.qs'])
        # QSharp compiler now generates a bitcode file,
        # i.e. no need to do llvm-as
        bc_file_name = base_name + '.bc'
@@ -390,7 +466,8 @@ def main(argv=None):
            sys.argv.remove('-qs-build-exe')
            qs_build_exe = '--build-exe'

        result = subprocess.run(['qsc', 'build', '--load', qs_qir_gen_dll, qs_build_exe, "--runtime", qs_rt_name, '--input', filename, '@CMAKE_INSTALL_PREFIX@/include/qsharp/QirCore.qs', '@CMAKE_INSTALL_PREFIX@/include/qsharp/QirTarget.qs', '--proj', base_name], check=True)
        # Execute qsc build command:
        os.system(build_cmd)
        llvm_bin_path = str(pathlib.Path(compiler).parent)
        result = subprocess.run([llvm_bin_path+'/llc', '-filetype=obj', bc_file_name ], check=True)
        sys.argv.remove(filename)

tools/driver/qsharp_utils.py

deleted100644 → 0
+0 −45
Original line number Diff line number Diff line
import os, tempfile, shutil

# Generate Q# build config by running the SDK
# Input: the source file directory where the build
# csproj will be saved to.
def generate_qsc_build_config(src_dir)
  temp_dir = tempfile.mkdtemp()
  # print(temp_dir)
  os.system('dotnet new console -lang Q# -o ' + temp_dir + ' -n test')

  import xml.etree.ElementTree as ET
  # Parse the SDK generated Project file
  tree = ET.parse(temp_dir + '/test.csproj')

  # get root element
  root = tree.getroot()
  print(root.tag, root.attrib, root.text)
  # run this target:
  root.attrib['DefaultTargets'] = 'PrepareQSharpCompile'
  propertyGroup = root.find('PropertyGroup')
  # enable QIR generation
  qir_gen = ET.Element('QirGeneration')
  qir_gen.text = 'true'
  propertyGroup.append(qir_gen)

  # Add a target to query the actual Build Command
  # to get the most accurate QSC executable:
  # <Target Name="PrintBuildCommand" BeforeTargets="PrepareQSharpCompile" >
  #   <WriteLinesToFile File="QscExe.cmd" Overwrite="true" Lines="$(QscExe)" />
  # </Target>

  export_qsc_cmd = ET.Element('WriteLinesToFile')
  export_qsc_cmd.attrib['File'] = 'QscExe.cmd'
  export_qsc_cmd.attrib['Overwrite'] = 'true'
  export_qsc_cmd.attrib['Lines'] = '$(QscExe)'

  export_qsc_cmd_target = ET.Element('Target')
  export_qsc_cmd_target.attrib['Name'] = 'PrintBuildCommand'
  export_qsc_cmd_target.attrib['BeforeTargets'] = 'PrepareQSharpCompile'
  export_qsc_cmd_target.append(export_qsc_cmd)

  root.append(export_qsc_cmd_target)
  tree.write(src_dir + '/output.csproj')
  # Delete the temp_directory:
  shutil.rmtree(temp_dir)