Ejemplo n.º 1
0
def main():
    args = parser.parse_args()

    # https://github.com/pypa/pip/blob/9.0.1/pip/__init__.py#L209
    if pip_main(["wheel", "-w", args.directory, "-r", args.input]):
        sys.exit(1)

    # Enumerate the .whl files we downloaded.
    def list_whls():
        dir = args.directory + '/'
        for root, unused_dirnames, filenames in os.walk(dir):
            for fname in filenames:
                if fname.endswith('.whl'):
                    yield os.path.join(root, fname)

    def whl_library(wheel):
        # Indentation here matters.  whl_library must be within the scope
        # of the function below.  We also avoid reimporting an existing WHL.
        return """
  if "{repo_name}" not in native.existing_rules():
    whl_library(
        name = "{repo_name}",
        whl = "@{name}//:{path}",
        requirements = "@{name}//:requirements.bzl",
    )""".format(name=args.name,
                repo_name=wheel.repository_name(),
                path=wheel.basename())

    whls = [Wheel(path) for path in list_whls()]

    with open(args.output, 'w') as f:
        f.write("""\
# Install pip requirements.
#
# Generated from {input}

load("@io_bazel_rules_python//python:whl.bzl", "whl_library")

def pip_install():
  {whl_libraries}

_packages = {{
  {mappings}
}}

all_packages = _packages.values()

def package(name):
  name = name.replace("-", "_")
  return _packages[name]
""".format(input=args.input,
           whl_libraries='\n'.join(map(whl_library, whls)),
           mappings=','.join([
               '"%s": "@%s//:pkg"' %
               (wheel.distribution(), wheel.repository_name())
               for wheel in whls
           ])))
Ejemplo n.º 2
0
def main():
    args = _parse_args()

    # https://github.com/pypa/pip/blob/9.0.1/pip/__init__.py#L209
    if _pip_main(["wheel", "-w", args.directory, "-r", args.input]):
        sys.exit(1)

    # Enumerate the .whl files we downloaded.
    def list_whl_files():
        dir_ = args.directory + '/'
        for root, unused_dirnames, filenames in os.walk(dir_):
            for fname in filenames:
                if fname.endswith('.whl'):
                    yield os.path.join(root, fname)

    wheels = [Wheel(path) for path in list_whl_files()]

    bzl_file_content = _make_bzl_file_content(
        wheels=wheels,
        reqs_repo_name=args.name,
        input_requirements_file_path=args.input)
    with open(args.output, 'w') as file_obj:
        file_obj.write(bzl_file_content)
Ejemplo n.º 3
0
def main():
    args = parser.parse_args()

    # https://github.com/pypa/pip/blob/9.0.1/pip/__init__.py#L209
    if pip_main(["wheel", "-w", args.directory, "-r", args.input]):
        sys.exit(1)

    # Enumerate the .whl files we downloaded.
    def list_whls():
        dir = args.directory + '/'
        for root, unused_dirnames, filenames in os.walk(dir):
            for fname in filenames:
                if fname.endswith('.whl'):
                    yield os.path.join(root, fname)

    whls = [Wheel(path) for path in list_whls()]
    possible_extras = determine_possible_extras(whls)

    def whl_library(wheel):
        # Indentation here matters.  whl_library must be within the scope
        # of the function below.  We also avoid reimporting an existing WHL.
        return """
  if "{repo_name}" not in native.existing_rules():
    whl_library(
        name = "{repo_name}",
        python_interpreter = "{python_interpreter}",
        whl = "@{name}//:{path}",
        requirements = "@{name}//:requirements.bzl",
        extras = [{extras}]
    )""".format(name=args.name,
                repo_name=wheel.repository_name(),
                python_interpreter=args.python_interpreter,
                path=wheel.basename(),
                extras=','.join([
                    '"%s"' % extra for extra in possible_extras.get(wheel, [])
                ]))

    whl_targets = ','.join([
        ','.join([
            '"%s": "@%s//:pkg"' %
            (whl.distribution().lower(), whl.repository_name())
        ] + [
            # For every extra that is possible from this requirements.txt
            '"%s[%s]": "@%s//:%s"' %
            (whl.distribution().lower(), extra.lower(), whl.repository_name(),
             extra) for extra in possible_extras.get(whl, [])
        ]) for whl in whls
    ])

    with open(args.output, 'w') as f:
        f.write("""\
# Install pip requirements.
#
# Generated from {input}

load("@io_bazel_rules_python//python:whl.bzl", "whl_library")

def pip_install():
  {whl_libraries}

_requirements = {{
  {mappings}
}}

all_requirements = _requirements.values()

def requirement(name):
  name_key = name.replace("-", "_").lower()
  if name_key not in _requirements:
    fail("Could not find pip-provided dependency: '%s'" % name)
  return _requirements[name_key]
""".format(input=args.input,
           whl_libraries='\n'.join(map(whl_library, whls)) if whls else "pass",
           mappings=whl_targets))
Ejemplo n.º 4
0
 def list_whls(dir):
   for root, _, filenames in os.walk(dir + "/"):
     for fname in filenames:
       if fname.endswith('.whl'):
         yield Wheel(os.path.join(root, fname))
Ejemplo n.º 5
0
def genbuild(args):
  whl = Wheel(args.whl)
  extra_deps = args.add_dependency or []
  drop_deps = {d: None for d in args.drop_dependency or []}

  external_deps = [d for d in itertools.chain(whl.dependencies(), extra_deps) if d not in drop_deps]

  contents = []
  add_build_content = args.add_build_content or []
  for name in add_build_content:
      with open(name) as f:
          contents.append(f.read() + '\n')
  contents = '\n'.join(contents)

  parser = whl.entrypoints()
  entrypoints_build = ''
  if parser:
      if parser.has_section('console_scripts'):
          for name, location in parser.items('console_scripts'):
              # Assumes it doesn't depend on extras. TODO(conrado): fix
              entrypoint_file = 'entrypoint_%s.py' % name
              with open(os.path.join(args.directory, entrypoint_file), 'w') as f:
                  f.write("""from %s import %s as main; main()""" % tuple(location.split(":")))
              attrs = []
              attrs += [("name", '"entrypoint_%s"' % name)]
              attrs += [("srcs", '["%s"]' % entrypoint_file)]
              attrs += [("main", '"%s"' % entrypoint_file)]
              if args.python_version:
                attrs += [("python_version", '"%s"' % args.python_version)]
              attrs += [("deps", '[":pkg"]')]
              entrypoints_build += """
py_binary(
    {attrs}
)
""".format(attrs=",\n    ".join(['{} = {}'.format(k, v) for k, v in attrs]))

  attrs = []
  if args.patches:
    attrs += [("patches", '["%s"]' % '", "'.join(args.patches))]
  if args.patch_tool:
    attrs += [("patch_tool", '"%s"' % args.patch_tool)]
  if args.patch_args:
    attrs += [("patch_args", '["%s"]' % '", "'.join(args.patch_args))]
  if args.patch_cmds:
    attrs += [("patch_cmds", '["%s"]' % '", "'.join(args.patch_cmds))]
  attrs += [("distribution", '"%s"' % whl.distribution().lower())]
  attrs += [("version", '"%s"' % whl.version())]
  attrs += [("wheel_size", "%s" % os.path.getsize(args.whl))]
  if args.python_version:
    attrs += [("python_version", '"%s"' % args.python_version)]

  with open(os.path.join(args.directory, 'BUILD'), 'w') as f:
    f.write("""
package(default_visibility = ["//visibility:public"])

load("@io_bazel_rules_python//python:python.bzl", "extract_wheel")
load("@{repository}//:requirements.bzl", "requirement")

filegroup(
  name = "wheel",
  data = ["{wheel}"],
)

extract_wheel(
    name = "extracted",
    wheel = "{wheel}",
    {attrs}
)

py_library(
    name = "pkg",
    imports = ["extracted"],
    deps = [
        ":extracted",{dependencies}
    ],
)

{extras}
{entrypoints_build}
{contents}""".format(
  wheel = whl.basename(),
  repository=args.repository,
  dependencies=''.join([
    ('\n        "%s",' % d) if d[0] == "@" else ('\n        requirement("%s"),' % d)
    for d in sorted(external_deps)
  ]),
  attrs=",\n    ".join(['{} = {}'.format(k, v) for k, v in attrs]),
  extras='\n\n'.join([
    """py_library(
    name = "{extra}",
    deps = [
        ":pkg",{deps}
    ],
)""".format(extra=extra,
            deps=','.join([
                'requirement("%s")' % dep
                for dep in sorted(whl.dependencies(extra))
            ]))
    for extra in args.extras or []
  ]),
  entrypoints_build=entrypoints_build,
  contents=contents))
Ejemplo n.º 6
0
def extract(args):
  whl = Wheel(args.whl)
  whl.expand(args.directory)
Ejemplo n.º 7
0
def build_wheel(distribution,
                version,
                directory,
                cache_key=None,
                build_dir=None,
                build_env=None,
                build_deps=None,
                sha256=None,
                pip_args=None,
                resolving=False):
  env = {}

  home = None
  if build_dir:
    home = build_dir.rstrip("/") + ".home"
    if os.path.isdir(home):
      shutil.rmtree(home)
    os.makedirs(home)
  else:
    home = tempfile.mkdtemp()

  cmd = ["wheel"]
  cmd += ["-w", directory]

  # Allowing "pip wheel" to download setup_requires packages with easy_install would
  # poke a hole to our wheel version locking scheme, making wheel builds non-deterministic.
  # Disable easy_install as instructed here:
  #   https://pip.pypa.io/en/stable/reference/pip_install/#controlling-setup-requires
  # We set HOME to the current directory so pip will look at this file; see:
  #   https://docs.python.org/2/install/index.html#distutils-configuration-files
  env["HOME"] = home
  with open(os.path.join(home, ".pydistutils.cfg"), "w") as f:
    f.write("[easy_install]\nallow_hosts = ''\n")

  for d in build_deps or []:
    Wheel(d).expand(home)

  # Process .pth files of the extracted build deps.
  with open(os.path.join(home, "sitecustomize.py"), "w") as f:
    f.write("import site; import os; site.addsitedir(os.path.dirname(__file__))")

  # Set PYTHONPATH so that all extracted buildtime dependencies are available.
  env["PYTHONPATH"] = ":".join(os.environ.get("PYTHONPATH", "").split(":") + [home])
  env["CFLAGS"] = " ".join([
      "-D__DATE__=\"redacted\"",
      "-D__TIMESTAMP__=\"redacted\"",
      "-D__TIME__=\"redacted\"",
      "-Wno-builtin-macro-redefine",
  ])

  # Set any other custom env variables the user wants to add to the wheel build.
  env.update(dict([x.split("=", 1) for x in build_env or []]))

  # For determinism, canonicalize distribution name to lowercase here, since, lo and
  # behold, the wheel contents may be different depending on the case passed to
  # "pip wheel" command...
  cmd += ["%s==%s" % (distribution.lower(), version)]

  cmd += ["--no-cache-dir"]
  cmd += ["--no-deps"]

  # Build the wheel in a deterministic path so that any debug symbols have stable
  # paths and the resulting wheel has a higher chance of being deterministic.
  if build_dir:
    if os.path.isdir(build_dir):
      shutil.rmtree(build_dir)
    cmd += ["--build", build_dir]

  cmd += pip_args or []

  locally_built = False
  with CaptureOutput() as output:
    if pip_main(cmd, env):
      print("pip command failed: " + str(cmd))
      sys.exit(1)
    if re.search(r"Running setup\.py bdist_wheel", output.stdout.getvalue().decode()):
      locally_built = True

  wheels = wheels_from_dir(directory)
  assert len(wheels) == 1
  wheel = wheels[0]

  if locally_built:
    # The wheel was built locally. For determinism, we need to strip timestamps
    # from the zip-file.
    strip_wheel(wheel)

  computed_sha256 = digest(wheel.path())

  if sha256 and computed_sha256 != sha256:
    if resolving:
      if locally_built:
        # If we built the wheel locally and the sha256 had changed from the previous one,
        # build the wheel again to make sure we get the same digest again.
        os.rename(wheel.path(), wheel.path() + ".0")
        if pip_main(cmd, env):
          sys.exit(1)
        strip_wheel(wheel)
        second_sha256 = digest(wheel.path())
        if computed_sha256 != second_sha256:
          os.rename(wheel.path(), wheel.path() + ".1")
          print("Wheel build not deterministic:")
          print("   %s.0: %s" % (wheel.path(), computed_sha256))
          print("   %s.1: %s" % (wheel.path(), second_sha256))
          sys.exit(1)
        os.remove(wheel.path() + ".0")
    else:
      # If the user supplied an expected sha256, the built wheel should match it.
      print("\033[0;33mWARNING:\033[0m Built wheel %s digest %s does not match expected digest %s." % (wheel.path(), computed_sha256, sha256))

  shutil.rmtree(home)
  return computed_sha256
Ejemplo n.º 8
0
def extract(args):
    # Extract the files into the current directory
    whl = Wheel(args.whl)
    whl.expand(args.directory)

    extra_deps = args.add_dependency or []
    drop_deps = {d: None for d in args.drop_dependency or []}

    imports = ['.']

    external_deps = [
        d for d in itertools.chain(whl.dependencies(), extra_deps)
        if d not in drop_deps
    ]

    contents = []
    add_build_content = args.add_build_content or []
    for name in add_build_content:
        with open(name) as f:
            contents.append(f.read() + '\n')
    contents = '\n'.join(contents)

    parser = whl.entrypoints()
    entrypoints_build = ''
    if parser:
        if parser.has_section('console_scripts'):
            for name, location in parser.items('console_scripts'):
                # Assumes it doesn't depend on extras. TODO(conrado): fix
                entrypoint_file = 'entrypoint_%s.py' % name
                with open(os.path.join(args.directory, entrypoint_file),
                          'w') as f:
                    f.write("""from %s import %s as main; main()""" %
                            tuple(location.split(":")))
                entrypoints_build += """
py_binary(
    name = "{entrypoint_name}",
    srcs = ["{entrypoint_file}"],
    main = "{entrypoint_file}",
    deps = [":pkg"],
)""".format(entrypoint_name='entrypoint_%s' % name,
                entrypoint_file=entrypoint_file)

    with open(os.path.join(args.directory, 'BUILD'), 'w') as f:
        f.write("""
package(default_visibility = ["//visibility:public"])

load("@{repository}//:requirements.bzl", "requirement")

filegroup(
  name = "wheel",
  data = ["{wheel}"],
)
py_library(
    name = "pkg",
    srcs = glob(["**/*.py"]),
    data = glob(["**/*"], exclude=["**/*.py", "**/* *", "*.whl", "BUILD", "WORKSPACE"]),
    # This makes this directory a top-level in the python import
    # search path for anything that depends on this.
    imports = [{imports}],
    deps = [
        {dependencies}
    ],
)
{extras}
{entrypoints_build}
{contents}""".format(wheel=whl.basename(),
                     repository=args.repository,
                     dependencies=''.join([
                         ('\n        "%s",' % d) if d[0] == "@" else
                         ('\n        requirement("%s"),' % d)
                         for d in sorted(external_deps)
                     ]),
                     imports=','.join(['"%s"' % i for i in sorted(imports)]),
                     extras='\n\n'.join([
                         """py_library(
    name = "{extra}",
    deps = [
        ":pkg",{deps}
    ],
)""".format(extra=extra,
            deps=','.join([
                'requirement("%s")' % dep
                for dep in sorted(whl.dependencies(extra))
            ])) for extra in args.extras or []
                     ]),
                     entrypoints_build=entrypoints_build,
                     contents=contents))