Enciclopedia de prueba

Informar un problema Ver fuente Noche}

Una especificación exhaustiva del entorno de ejecución de pruebas.

Información general

El lenguaje de compilación de Bazel incluye reglas que se pueden usar para definir programas de prueba automatizados en muchos lenguajes.

Las pruebas se ejecutan con bazel test.

Los usuarios también pueden ejecutar objetos binarios de prueba de forma directa. Esto está permitido, pero no se recomienda, ya que esa invocación no cumplirá con los mandatos que se describen a continuación.

Las pruebas deben ser herméticas, es decir, deben acceder solo a los recursos en los que tienen una dependencia declarada. Si las pruebas no son debidamente herméticas, no dan resultados reproducibles históricamente. Esto podría ser un problema importante para el hallazgo del culpable (determinar qué cambio interrumpió una prueba), la auditabilidad de la ingeniería de lanzamientos y el aislamiento de recursos de las pruebas (los frameworks de pruebas automatizadas no deben generar un DDoS en un servidor porque algunas pruebas generan mensajes).

Objetivo

El objetivo de esta página es establecer formalmente el entorno de ejecución y el comportamiento esperado de las pruebas de Bazel. También impondrá requisitos al ejecutor de pruebas y al sistema de compilación.

La especificación del entorno de pruebas ayuda a los autores de pruebas a evitar depender de un comportamiento no especificado y, por lo tanto, brinda a la infraestructura de pruebas más libertad para realizar cambios de implementación. La especificación ajusta algunos agujeros que permiten en la actualidad pasar muchas pruebas a pesar de no ser debidamente herméticas, determinísticas y reentrantes.

Esta página tiene fines normativos y confiables. Si esta especificación y el comportamiento implementado del ejecutor de pruebas no coinciden, la especificación tiene prioridad.

Especificación propuesta

Las palabras clave “DEBE”, “NO DEBE”, “OBLIGATORIO”, “DEBERÍA”, “NO DEBERÁ”, “DEBERÍA”, “NO DEBERÍA”, “RECOMENDADO”, “PUEDE” y “OPCIONAL” se deben interpretar como se describe en el documento RFC 2119 de la IETF.

Propósito de las pruebas

El propósito de las pruebas de Bazel es confirmar alguna propiedad de los archivos de origen que se registraron en el repositorio. (En esta página, los “archivos de origen” incluyen datos de prueba, resultados dorados y todo lo que se mantenga bajo el control de versión). Un usuario escribe una prueba para confirmar un invariante que espera mantener. Otros usuarios ejecutan la prueba más tarde para verificar si el invariante se rompió. Si la prueba depende de otras variables que no sean archivos de origen (no herméticas), su valor se reduce, ya que los usuarios posteriores no pueden estar seguros de que sus cambios sean erróneos cuando la prueba deja de pasar.

Por lo tanto, el resultado de una prueba solo debe depender de lo siguiente:

  • archivos de origen en los que la prueba tiene una dependencia declarada.
  • productos del sistema de compilación en el que la prueba tiene una dependencia declarada.
  • recursos cuyo comportamiento está garantizado por el ejecutor de pruebas para que permanezca constante

Actualmente, este comportamiento no se aplica de manera forzosa. Sin embargo, los ejecutores de pruebas se reservan el derecho de agregar esa aplicación en el futuro.

Rol del sistema de compilación

Las reglas de prueba son análogas a las reglas binarias en el sentido de que cada una debe producir un programa ejecutable. Para algunos lenguajes, este es un programa de stub que combina un agente específico del lenguaje con el código de prueba. Las reglas de prueba también deben producir otros resultados. Además del ejecutable de prueba principal, el ejecutor de pruebas necesitará un manifiesto de runfiles, archivos de entrada que deberían estar disponibles para la prueba en el entorno de ejecución, y podría necesitar información sobre el tipo, el tamaño y las etiquetas de una prueba.

El sistema de compilación puede usar los archivos de ejecución para entregar código y datos. (esto se puede usar como optimización para hacer que cada objeto binario de prueba sea más pequeño compartiendo archivos entre pruebas, por ejemplo, mediante el uso de vínculos dinámicos). El sistema de compilación debe asegurarse de que el ejecutable generado cargue estos archivos mediante la imagen de archivos de ejecución proporcionada por el ejecutor de pruebas, en lugar de referencias codificadas a ubicaciones absolutas en el árbol de fuente o de salida.

Rol del ejecutor de pruebas

Desde el punto de vista del ejecutor de pruebas, cada prueba es un programa que se puede invocar con execve(). Puede haber otras maneras de ejecutar pruebas; por ejemplo, un IDE podría permitir la ejecución de pruebas de Java en proceso. Sin embargo, el resultado de ejecutar la prueba como un proceso independiente debe considerarse autorizado. Si un proceso de prueba se ejecuta hasta su finalización y finaliza normalmente con un código de salida cero, significa que la prueba fue exitosa. Cualquier otro resultado se considera una prueba fallida. En particular, escribir cualquiera de las cadenas PASS o FAIL en stdout no tiene importancia para el ejecutor de pruebas.

Si una prueba tarda demasiado en ejecutarse, supera algún límite de recursos, o el ejecutor de pruebas detecta un comportamiento prohibido, puede optar por finalizar la prueba y tratar la ejecución como una falla. El ejecutor no debe informar que la prueba fue exitosa después de enviar una señal al proceso de prueba ni a sus elementos secundarios.

El objetivo de prueba completo (no los métodos o pruebas individuales) tiene una cantidad de tiempo limitada para ejecutarse hasta su finalización. El límite de tiempo de una prueba se basa en su atributo timeout de acuerdo con la siguiente tabla:

tiempo de espera agotado Límite de tiempo (s)
short 60
moderadas 300
long 900
eterno 3,600

Las pruebas que no especifican un tiempo de espera de forma explícita tienen uno implícito basado en el size de la prueba de la siguiente manera:

tamaño Etiqueta de tiempo de espera implícita
poco a poco short
medium moderadas
grandes long
enorme eterno

Una prueba “grande” sin una configuración explícita de tiempo de espera se asignará 900 segundos para ejecutarse. Una prueba “media” con un tiempo de espera de “corto” se asignará a 60 segundos.

A diferencia de timeout, size también determina el uso máximo supuesto de otros recursos (como la RAM) cuando se ejecuta la prueba de manera local, como se describe en Definiciones comunes.

Todas las combinaciones de etiquetas size y timeout son legales, por lo que se puede declarar que una prueba “enorme” tenga un tiempo de espera de “corto”. Es probable que haga cosas muy horribles muy rápido.

Las pruebas pueden ser arbitrariamente rápidas, independientemente del tiempo de espera. Una prueba no se penaliza por un tiempo de espera demasiado generoso, aunque se puede emitir una advertencia:, por lo general, debes establecer tu tiempo de espera lo más ajustado posible sin incurrir en incurrencias inexactas.

El tiempo de espera de la prueba se puede anular con la marca --test_timeout de Bazel cuando se ejecuta de forma manual en condiciones que se sabe que son lentas. Los valores --test_timeout están en segundos. Por ejemplo, --test_timeout=120 establece el tiempo de espera de la prueba en dos minutos.

También se recomienda un límite inferior para los tiempos de espera de las pruebas, como se indica a continuación:

tiempo de espera agotado Tiempo mínimo (s)
short 0
moderadas 30
long 300
eterno 900

Por ejemplo, si una prueba “moderada” se completa en 5.5 s, considera configurar timeout = "short" o size = "small". Con la opción de línea de comandos --test_verbose_timeout_warnings de bazel, se mostrarán las pruebas cuyo tamaño especificado es demasiado grande.

Los tamaños de prueba y los tiempos de espera se especifican en el archivo BUILD según la especificación que se indica aquí. Si no se especifica, el tamaño de una prueba se establecerá de forma predeterminada en “medium”.

Si se cierra el proceso principal de una prueba, pero algunos de sus elementos secundarios aún se están ejecutando, el ejecutor de pruebas debe considerar que la ejecución se completó y contarla como éxito o fracaso según el código de salida observado en el proceso principal. El ejecutor de pruebas puede finalizar cualquier proceso suelto. Las pruebas no deben filtrar los procesos de esta manera.

Fragmentación de pruebas

Las pruebas se pueden paralelizar a través de la fragmentación de pruebas. Consulta --test_sharding_strategy y shard_count para habilitar la fragmentación de pruebas. Cuando la fragmentación está habilitada, el ejecutor de pruebas se inicia una vez por fragmento. La variable de entorno TEST_TOTAL_SHARDS es la cantidad de fragmentos, y TEST_SHARD_INDEX es el índice de fragmentos, que comienza en 0. Los ejecutores usan esta información para seleccionar qué pruebas ejecutar, por ejemplo, con una estrategia round robin. No todos los ejecutores de pruebas admiten la fragmentación. Si un ejecutor admite la fragmentación, debe crear o actualizar la fecha de la última modificación del archivo especificado por TEST_SHARD_STATUS_FILE. De lo contrario, si --incompatible_check_sharding_support está habilitado, Bazel no aprobará la prueba si está fragmentada.

Condiciones iniciales

Cuando se ejecuta una prueba, el ejecutor de pruebas debe establecer ciertas condiciones iniciales.

El ejecutor de pruebas debe invocar cada prueba con la ruta de acceso a la prueba ejecutable en argv[0]. Esta ruta de acceso debe ser relativa y estar debajo del directorio actual de la prueba (que se encuentra en el árbol de archivos de ejecución, consulta a continuación). El ejecutor de pruebas no debe pasar ningún otro argumento a una prueba, a menos que el usuario lo solicite explícitamente.

El bloque de entorno inicial debe estar compuesto de la siguiente manera:

Variable Valor Estado
HOME valor de $TEST_TMPDIR recomendado
LANG unset obligatorio
LANGUAGE unset obligatorio
LC_ALL unset obligatorio
LC_COLLATE unset obligatorio
LC_CTYPE unset obligatorio
LC_MESSAGES unset obligatorio
LC_MONETARY unset obligatorio
LC_NUMERIC unset obligatorio
LC_TIME unset obligatorio
LD_LIBRARY_PATH lista de directorios separados por dos puntos que contienen bibliotecas compartidas opcional
JAVA_RUNFILES valor de $TEST_SRCDIR obsoleto
LOGNAME valor de $USER obligatorio
PATH /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:. recomendado
PWD $TEST_SRCDIR/workspace-name recomendado
SHLVL 2 recomendado
TEST_INFRASTRUCTURE_FAILURE_FILE ruta de acceso absoluta a un archivo privado en un directorio que admita escritura (este archivo solo debe usarse para informar fallas que se originen en la infraestructura de pruebas, no como un mecanismo general para informar fallas inestables de las pruebas. En este contexto, la infraestructura de pruebas se define como sistemas o bibliotecas que no son específicos de pruebas, pero que pueden provocar fallas en las pruebas debido a un mal funcionamiento. La primera línea es el nombre del componente de la infraestructura de pruebas que causó la falla, y la segunda es una descripción legible de la falla. Se ignorarán las líneas adicionales). opcional
TEST_LOGSPLITTER_OUTPUT_FILE Ruta de acceso absoluta a un archivo privado en un directorio que admite escritura (se usa para escribir el registro de protobuffer de Logsplitter) opcional
TEST_PREMATURE_EXIT_FILE ruta de acceso absoluta a un archivo privado en un directorio que admite escritura (se usa para capturar llamadas a exit()) opcional
TEST_RANDOM_SEED Si se usa la opción --runs_per_test, TEST_RANDOM_SEED se establece en run number (comienza con 1) para cada ejecución de prueba individual. opcional
TEST_RUN_NUMBER Si se usa la opción --runs_per_test, TEST_RUN_NUMBER se establece en run number (comienza con 1) para cada ejecución de prueba individual. opcional
TEST_TARGET El nombre del destino que se está probando opcional
TEST_SIZE La prueba size opcional
TEST_TIMEOUT La prueba timeout en segundos opcional
TEST_SHARD_INDEX índice de fragmentos, si se usa sharding opcional
TEST_SHARD_STATUS_FILE ruta de acceso al archivo que se debe tocar para indicar compatibilidad con sharding opcional
TEST_SRCDIR ruta de acceso absoluta a la base del árbol de archivos de ejecución obligatorio
TEST_TOTAL_SHARDS en total shard count, si se usa sharding opcional
TEST_TMPDIR ruta de acceso absoluta a un directorio privado que admite escritura obligatorio
TEST_WORKSPACE el nombre del espacio de trabajo del repositorio local opcional
TEST_UNDECLARED_OUTPUTS_DIR ruta de acceso absoluta a un directorio privado que admite escritura (se usa para escribir resultados de prueba no declarados) opcional
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR ruta de acceso absoluta a un directorio privado con escritura (se usa para escribir archivos .part y .pb de anotaciones de resultados de prueba no declarados). opcional
TEST_WARNINGS_OUTPUT_FILE ruta de acceso absoluta a un archivo privado en un directorio que admite escritura (se usa para escribir advertencias del objetivo de prueba) opcional
TESTBRIDGE_TEST_ONLY valor de --test_filter, si se especifica opcional
TZ UTC obligatorio
USER valor de getpwuid(getuid())->pw_name obligatorio
XML_OUTPUT_FILE Ubicación en la que las acciones de prueba deben escribir un archivo de salida XML de resultados de la prueba. De lo contrario, Bazel generará un archivo de salida XML predeterminado que une el registro de pruebas como parte de la acción de prueba. El esquema XML se basa en el esquema de resultados de la prueba JUnit. opcional
BAZEL_TEST Indica que bazel test controla el ejecutable de la prueba. obligatorio

El entorno puede contener entradas adicionales. Las pruebas no deben depender de la presencia, ausencia o valor de ninguna variable de entorno que no se haya mencionado anteriormente.

El directorio de trabajo inicial debe ser $TEST_SRCDIR/$TEST_WORKSPACE.

No se especifican el ID del proceso actual, el ID del grupo de procesos, el ID de la sesión y el ID del proceso superior. El proceso puede o no ser un líder de grupo de procesos o de sesión. El proceso puede o no tener una terminal de control. El proceso puede tener cero o más procesos secundarios en ejecución o sin procesar. El proceso no debe tener varios subprocesos cuando el código de prueba obtiene el control.

El descriptor de archivos 0 (stdin) debe estar abierto para lectura, pero no se especifica qué se adjunta. Las pruebas no deben leer de estos datos. Los descriptores de archivos 1 (stdout) y 2 (stderr) deben estar abiertos para escritura, pero no se especifican a qué se adjuntan. Podría ser una terminal, una canalización, un archivo normal o cualquier otra cosa en la que se puedan escribir caracteres. Pueden compartir una entrada en la tabla abierta de archivos (lo que significa que no pueden buscar de forma independiente). Las pruebas no deben heredar ningún otro descriptor de archivos abierto.

La umask inicial debe ser 022 o 027.

No debe haber ninguna alarma ni temporizador de intervalo pendiente.

La máscara inicial de las señales bloqueadas debe estar vacía. Todos los indicadores se configurarán con su acción predeterminada.

Los límites de recursos iniciales, tanto flexibles como estrictos, se deben establecer de la siguiente manera:

Recurso Límite
RLIMIT_AS ilimitados
RLIMIT_CORE sin especificar
RLIMIT_CPU ilimitados
RLIMIT_DATA ilimitados
RLIMIT_FSIZE ilimitados
RLIMIT_LOCKS ilimitados
RLIMIT_MEMLOCK ilimitados
RLIMIT_MSGQUEUE sin especificar
RLIMIT_NICE sin especificar
RLIMIT_NOFILE al menos, 1,024
RLIMIT_NPROC sin especificar
RLIMIT_RSS ilimitados
RLIMIT_RTPRIO sin especificar
RLIMIT_SIGPENDING sin especificar
RLIMIT_STACK ilimitado o 2044 KB <= rlim <= 8192 KB

No se especifican los tiempos de proceso iniciales (como lo muestra times()) y el uso de recursos (como lo muestra getrusage()).

No se especifican la política de programación inicial ni la prioridad.

Función del sistema host

Además de los aspectos del contexto del usuario bajo el control directo del ejecutor de pruebas, el sistema operativo en el que se ejecutan las pruebas debe cumplir con ciertas propiedades para que una ejecución de prueba sea válida.

Sistema de archivos

El directorio raíz observado por una prueba puede o no ser el directorio raíz real.

Se debe activar /proc.

Todas las herramientas de compilación deben estar presentes en las rutas de acceso absolutas en /usr que usa una instalación local.

Es posible que las rutas que comienzan con /home no estén disponibles. Las pruebas no deben acceder a ninguna de esas rutas.

Se debe poder escribir en /tmp, pero las pruebas deben evitar el uso de estas rutas de acceso.

Las pruebas no deben suponer que ninguna ruta de acceso constante está disponible para uso exclusivo.

Las pruebas no deben suponer que los atimes están habilitados para cualquier sistema de archivos activado.

Usuarios y grupos

Deben existir la raíz del usuario, nadie y la prueba de unidades. Los grupos raíz, eng y nadie deben existir.

Las pruebas se deben ejecutar como un usuario no raíz. Los IDs de usuario reales y efectivos deben ser iguales; igual que los IDs de grupo. Aparte de esto, no se especifican el ID de usuario, el ID de grupo, el nombre de usuario y el nombre del grupo actuales. El conjunto de IDs de grupo complementarios no se especifica.

El ID de usuario y el ID de grupo actuales deben tener los nombres correspondientes que se pueden recuperar con getpwuid() y getgrgid(). Lo mismo podría suceder con los IDs de grupo complementarios.

El usuario actual debe tener un directorio principal. Es posible que no se pueda escribir en ella. Las pruebas no deben intentar escribir en él.

Herramientas de redes

El nombre de host no está especificado. Puede contener un punto o no. Cuando se resuelve el nombre de host, se debe proporcionar una dirección IP del host actual. También debe funcionar la resolución del corte del nombre de host después del primer punto. Debe resolverse el nombre de host localhost.

Otros recursos

Las pruebas reciben al menos un núcleo de CPU. Puede haber otros disponibles, pero esto no está garantizado. No se especificaron otros aspectos de rendimiento de este núcleo. Puedes aumentar la reserva a una mayor cantidad de núcleos de CPU si agregas la etiqueta “cpu:n” (donde n es un número positivo) a una regla de prueba. Aunque una máquina tenga menos núcleos de CPU totales de los solicitados, Bazel ejecutará la prueba de todos modos. Si una prueba usa la fragmentación, cada fragmento individual reservará la cantidad de núcleos de CPU especificada aquí.

Las pruebas pueden crear subprocesos, pero no grupos de procesos ni sesiones.

Existe un límite para la cantidad de archivos de entrada que puede consumir una prueba. Este límite está sujeto a cambios, pero, por el momento, se encuentra en el rango de decenas de miles de entradas.

Hora y fecha

No se especificaron la fecha ni la hora actuales. No se especifica la zona horaria del sistema.

X: Es posible que Windows no esté disponible. Las pruebas que necesitan un servidor X deben iniciar Xvfb.

Prueba la interacción con el sistema de archivos

Todas las rutas de acceso a archivos especificadas en las variables de entorno de prueba apuntan a algún lugar del sistema de archivos local, a menos que se especifique lo contrario.

Las pruebas deben crear archivos solo dentro de los directorios especificados por $TEST_TMPDIR y $TEST_UNDECLARED_OUTPUTS_DIR (si se establecen).

Inicialmente, estos directorios estarán vacíos.

Las pruebas no deben intentar quitar, aplicar chmod ni alterar de otra forma estos directorios.

Estos directorios pueden ser vínculos simbólicos.

No se especifica el tipo de sistema de archivos de $TEST_TMPDIR/..

Las pruebas también pueden escribir archivos .part en $TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR para anotar los archivos de salida no declarados.

En casos excepcionales, es posible que una prueba se vea forzada a crear archivos en /tmp. Por ejemplo, los límites de longitud de la ruta de acceso para los sockets de dominio Unix suelen requerir la creación del socket en /tmp. Bazel no podrá hacer un seguimiento de esos archivos. La prueba en sí debe ser hermética, de usar rutas únicas para evitar conflictos con otros procesos que ejecutan pruebas y que no lo son de forma simultánea, y de limpiar los archivos que crea en /tmp.

Algunos frameworks de prueba populares, como JUnit4 TemporaryFolder o Go TempDir, tienen sus propias formas de crear un directorio temporal en /tmp. Estos frameworks de prueba incluyen funciones que borran archivos en /tmp, por lo que puedes usarlos aunque creen archivos fuera de TEST_TMPDIR.

Las pruebas deben acceder a las entradas a través del mecanismo de runfiles o de otras partes del entorno de ejecución destinadas específicamente a que los archivos de entrada estén disponibles.

Las pruebas no deben acceder a otras salidas del sistema de compilación en rutas inferidas de la ubicación de su propio ejecutable.

No se especifica si el árbol de archivos de ejecución contiene archivos regulares, vínculos simbólicos o una mezcla. El árbol de archivos de ejecución puede contener symlinks a directorios. Las pruebas deben evitar el uso de rutas que contengan componentes .. dentro del árbol de archivos de ejecución.

No se debe poder escribir ningún directorio, archivo ni symlink dentro del árbol de archivos de ejecución (incluidas las rutas de acceso que recorren symlinks). De acuerdo con esto, el directorio de trabajo inicial no debe poder escribirse. Las pruebas no deben suponer que alguna parte de los archivos de ejecución se puede escribir o que es propiedad del usuario actual (por ejemplo, chmod y chgrp pueden fallar).

El árbol de archivos de ejecución (incluidas las rutas de acceso que atraviesan symlinks) no debe cambiar durante la ejecución de la prueba. Los directorios superiores y las activaciones del sistema de archivos no deben cambiar de ninguna manera, lo que afecta el resultado de la resolución de una ruta de acceso dentro del árbol de archivos de ejecución.

Para detectar la salida anticipada, una prueba puede crear un archivo en la ruta que especifica TEST_PREMATURE_EXIT_FILE al inicio y quitarlo al salir. Si Bazel ve el archivo cuando finaliza la prueba, supondrá que la prueba se cerró de forma prematura y la marcará como fallida.

Convenciones de etiquetas

Algunas etiquetas de las reglas de prueba tienen un significado especial. Consulta también la Enciclopedia de compilación de Bazel sobre el atributo tags.

Etiqueta Significado
exclusive no ejecutar ninguna otra prueba al mismo tiempo
external la prueba tiene una dependencia externa; inhabilita el almacenamiento en caché de pruebas
large Convención test_suite; conjunto de pruebas de nivel superior
manual * No incluyas el destino de prueba en patrones de destino con comodines, como :..., :* o :all.
medium Convención test_suite; conjunto de pruebas de nivel intermedio
small Convención test_suite; conjunto de pruebas pequeñas
smoke Convención test_suite; significa que debe ejecutarse antes de confirmar cambios de código en el sistema de control de versión

Archivos de ejecución

En el siguiente ejemplo, supongamos que hay una regla *_binary() etiquetada como //foo/bar:unittest, con una dependencia en el tiempo de ejecución de la regla etiquetada //deps/server:server.

Ubicación

El directorio runfiles de un //foo/bar:unittest de destino es el directorio $(WORKSPACE)/$(BINDIR)/foo/bar/unittest.runfiles. Esta ruta se conoce como runfiles_dir.

Dependencias

El directorio de archivos de ejecución se declara como una dependencia en tiempo de compilación de la regla *_binary(). El directorio de archivos de ejecución depende del conjunto de archivos BUILD que afectan a la regla *_binary() o cualquiera de sus dependencias de tiempo de compilación o ejecución. La modificación de los archivos de origen no afecta la estructura del directorio runfiles y, por lo tanto, no activa ninguna compilación.

Contenido

El directorio runfiles contiene lo siguiente:

  • Enlaces simbólicos a dependencias del entorno de ejecución: Cada OutputFile y CommandRule que es una dependencia en el tiempo de ejecución de la regla *_binary() se representa con un symlink en el directorio runfiles. El nombre del symlink es $(WORKSPACE)/package_name/rule_name. Por ejemplo, el symlink para el servidor se llamaría $(WORKSPACE)/deps/server/server y la ruta completa sería $(WORKSPACE)/foo/bar/unittest.runfiles/$(WORKSPACE)/deps/server/server. El destino del symlink es el OutputFileName() del OutputFile o CommandRule, expresado como una ruta de acceso absoluta. Por lo tanto, el destino del symlink podría ser $(WORKSPACE)/linux-dbg/deps/server/42/server.
  • Vínculos simbólicos a subarchivos de ejecución: Por cada *_binary() Z que sea una dependencia de tiempo de ejecución de *_binary() C, hay un segundo vínculo en el directorio de archivos de ejecución de C a los archivos de ejecución de Z. El nombre del symlink es $(WORKSPACE)/package_name/rule_name.runfiles. El destino del symlink es el directorio de archivos de ejecución. Por ejemplo, todos los subprogramas comparten un directorio de runfiles en común.