Как я могу создать пользовательские правила, используя выходные данные workspace_status_command? - PullRequest
0 голосов
/ 01 июня 2018

Флаг bazel build --workspace_status_command поддерживает вызов сценария для извлечения, например, метаданных из хранилища, это также называется штампом сборки и доступно в правилах, таких как java_binary.

Я хотел бы создатьПользовательское правило, использующее эти метаданные.Я хочу использовать это для общей функции поддержки.Он должен получить версию git и некоторые другие атрибуты и создать выходной файл version.go, пригодный для использования в качестве зависимости.

Итак, я начал исследовать правила в различных репозиториях Bazel.

Правила вродеrules_docker поддерживает печать с stamp в container_image и позволяет ссылаться на вывод статуса в атрибутах.

rules_go поддерживает его в атрибуте x_defs go_binary.

Это было бы идеально для моей цели, и я закопался ...

Похоже, я могу получить то, что хочу, с помощью ctx.actions.expand_template, используя записи в ctx.info_fileили ctx.version_file в качестве словаря для substitutions.Но я не понял, как получить словарь этих файлов.И эти два файла кажутся «неофициальными», они не являются частью документации ctx.

Опираясь на то, что я уже выяснил: как получить dict на основе вывода команды status?

Если это невозможно, какой самый короткий / простой способ получить доступ к выводу workspace_status_command из пользовательских правил?

Ответы [ 2 ]

0 голосов
/ 05 июня 2018

Опираясь на ответ Ондрея, я теперь использую что-то подобное (адаптировано в редакторе SO, может содержать небольшие ошибки):

tools/bazel.rc:

build --workspace_status_command=tools/workspace_status.sh

tools/workspace_status.sh:

echo STABLE_GIT_REV $(git rev-parse HEAD)

version.bzl:

_VERSION_TEMPLATE_SH = """
set -e -u -o pipefail

while read line; do
  export "${line% *}"="${line#* }"
done <"$INFILE" \
&& cat <<EOF >"$OUTFILE"
{ "ref": "${STABLE_GIT_REF}"
, "service": "${SERVICE_NAME}"
}
EOF
"""

def _commit_info_impl(ctx):
  ctx.actions.run_shell(
      outputs = [ctx.outputs.outfile],
      inputs = [ctx.info_file],
      progress_message = "Generating version file: " + ctx.label.name,
      command = _VERSION_TEMPLATE_SH,
      env = {
        'INFILE': ctx.info_file.path,
        'OUTFILE': ctx.outputs.version_go.path,
        'SERVICE_NAME': ctx.attr.service,
      },
  )

commit_info = rule(
    implementation = _commit_info_impl,
    attrs = {
      'service': attr.string(
          mandatory = True,
          doc = 'name of versioned service',
      ),
    },
    outputs = {
      'outfile': 'manifest.json',
    },
)
0 голосов
/ 03 июня 2018

Я был именно там, где вы есть, и я пошел по пути, который вы начали исследовать.Я генерирую описание JSON, которое также включает в себя информацию, собранную из git для упаковки с результатом, и в итоге я сделал что-то вроде этого:

def _build_mft_impl(ctx):
    args = ctx.actions.args()
    args.add('-f')
    args.add(ctx.info_file)
    args.add('-i')
    args.add(ctx.files.src)
    args.add('-o')
    args.add(ctx.outputs.out)
    ctx.actions.run(
        outputs = [ctx.outputs.out],
        inputs = ctx.files.src + [ctx.info_file],
        arguments = [args],
        progress_message = "Generating manifest: " + ctx.label.name,
        executable = ctx.executable._expand_template,
    )

def _get_mft_outputs(src):
    return {"out": src.name[:-len(".tmpl")]}

build_manifest = rule(
        implementation = _build_mft_impl,
        attrs = {
            "src": attr.label(mandatory=True,
                              allow_single_file=[".json.tmpl", ".json_tmpl"]),
            "_expand_template": attr.label(default=Label("//:expand_template"),
                                           executable=True,
                                           cfg="host"),
        },
        outputs = _get_mft_outputs,
    )

//:expand_template - это метка в моем случае, указывающая на py_binaryвыполняя само преобразование.Я был бы счастлив узнать о лучшем (более родном, меньшем количестве прыжков) способе сделать это, но (пока) я пошел: это работает.Несколько комментариев о подходе и ваших проблемах:

  • AFAIK, который вы не можете прочитать (файл и выполнить операции в жаворонке) сам ...
  • ... говоря о том, что этовероятно, неплохо было бы разделить преобразование (инструмент) и описание сборки (bazel) в любом случае.
  • Можно обсудить, что составляет официальную документацию, но ctx.info_file может не появиться в справочном руководстве, этозадокументировано в исходном дереве.:) Это относится и к другим областям (и я надеюсь, что это не потому, что эти интерфейсы тоже пока не приняты).

Ради полноты в src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleContextApi.java есть:

@SkylarkCallable(
  name = "info_file",
  structField = true,
  documented = false,
  doc =
  "Returns the file that is used to hold the non-volatile workspace status for the "
      + "current build request."
)
public FileApi getStableWorkspaceStatus() throws InterruptedException, EvalException;

РЕДАКТИРОВАТЬ: несколько дополнительных деталей, как указано в комментарии.

В моем workspace_status.sh у меня будет, например, следующая строка:

echo STABLE_GIT_REF $(git log -1 --pretty=format:%H)

В моем файле .json.tmpl у меня было бы:

"ref": "${STABLE_GIT_REF}",

Я выбрал замену текста, похожую на оболочку, поскольку он интуитивно понятен для многих пользователей, а также прост в сопоставлении.

Что касается замены, соответствующая (CLI исключается из этого) часть фактического кода будет:

def get_map(val_file):
    """
    Return dictionary of key/value pairs from ``val_file`.
    """
    value_map = {}

    for line in val_file:
        (key, value) = line.split(' ', 1)
        value_map.update(((key, value.rstrip('\n')),))
    return value_map


def expand_template(val_file, in_file, out_file):
    """
    Read each line from ``in_file`` and write it to ``out_file`` replacing all
    ${KEY} references with values from ``val_file``.
    """
    def _substitue_variable(mobj):
        return value_map[mobj.group('var')]
    re_pat = re.compile(r'\${(?P<var>[^} ]+)}')
    value_map = get_map(val_file)
    for line in in_file:
        out_file.write(re_pat.subn(_substitue_variable, line)[0])

EDIT2: Вот как скрипт Python выглядит так, как явыставить скрипт python остальным базелам.

py_binary(
    name = "expand_template",
    main = "expand_template.py",
    srcs = ["expand_template.py"],
    visibility = ["//visibility:public"],
)
...