Tutorial

Cómo usar ldflags para configurar la información de versión de aplicaciones de Go

GoDevelopment

Introducción

Al implementar aplicaciones en un entorno de producción, la creación de binarios con información de versión y otros metadatos mejorará sus procesos de monitoreo, registro y depuración al agregar información de identificación para ayudar a realizar un seguimiento de sus compilaciones con el tiempo. Esta información de versión, a menudo, puede incluir datos muy dinámicos, como el tiempo de compilación, la máquina o el usuario que compiló el binario y el sistema de control de versiones (VCS) con el que se creó, entre otros. Debido a que estos valores cambian constantemente, codificar estos datos directamente en el código fuente y modificarlos antes de cada compilación nueva es tedioso y está expuesto errores: los archivos de código fuente se pueden mover y las variables y constantes pueden cambiar los archivos a lo largo del desarrollo, lo que interrumpe el proceso de compilación.

Una manera de resolver esto en Go es usar -ldflags con el comando go build para insertar información dinámica en el binario en el tiempo de compilación, sin necesidad de modificar el código fuente. En este indicador, ld significa enlazador, que es el programa que vincula las diferentes piezas del código fuente compilado en el binario final. Por lo tanto, ldflags quiere decir indicadores de enlazador. Se denomina de esta manera porque pasa un indicador al enlazador subyacente de la cadena de herramientas de Go, cmd/link, que le permite cambiar los valores de los paquetes importados en el tiempo de compilación desde la línea de comandos.

En este tutorial, usará -ldflags para cambiar el valor de las variables en el tiempo de compilación e introducir su propia información dinámica en un binario usando una aplicación de muestra que imprime información de versión en la pantalla.

Requisitos previos

Para seguir el ejemplo de este artículo, necesitará lo siguiente:

Crear su aplicación de muestra

Antes de usar ldflags para introducir datos dinámicos, primero, necesita una aplicación para insertar la información. En este paso, creará esta aplicación, que, en esta etapa, solo imprimirá información de control de versiones estática. Crearemos esa aplicación ahora.

En su directorio src, cree un directorio que lleve el nombre de su aplicación. En ese tutorial, se usará el nombre de aplicaciónapp:

  • mkdir app

Cambie su directorio de trabajo a esta carpeta:

  • cd app

A continuación, usando el editor de texto que prefiera, cree el punto de entrada de su programa, main.go:

  • nano main.go

Ahora, haga que su aplicación imprima información de versión añadiendo el siguiente contenido:

app/main.go
package main

import (
    "fmt"
)

var Version = "development"

func main() {
    fmt.Println("Version:\t", Version)
}

Dentro de la función main(), declaró la variable Version, luego imprimió la cadena Version: seguida de un carácter de tabulación, \t, y por último la variable declarada.

En este punto, la variable Version se define como development, que será la versión predeterminada de esta aplicación. Posteriormente, cambiará este valor para que sea un número de versión oficial, que se dispondrá conforme al formato de control de versiones semántico.

Guarde el archivo y ciérrelo. Una vez hecho esto, cree y ejecute la aplicación para confirmar que imprima la versión correcta:

  • go build
  • ./app

Verá lo siguiente:

Output
  • Version: development

Ahora, dispondrá de una aplicación que imprime información de versión predeterminada, pero todavía no cuenta con una forma de transmitir información de versión actual en el tiempo de compilación. En el siguiente paso, usará -ldflags y go build para resolver este problema.

Usar ldflags con go build

Como se mencionó anteriormente, ldflags significa *indicadores de enlazador *y se utiliza para pasar indicadores al enlazador subyacente de la cadena de herramientas de Go. Esto funciona de acuerdo con la siguiente sintaxis:

  • go build -ldflags="-flag"

En este ejemplo, pasamos flag al comando go tool link subyacente que se ejecuta como parte de go build. En este comando, se utilizan comillas dobles alrededor del contenido que se pasa a ldflags para evitar romper sus caracteres o la presencia de caracteres que la línea de comandos podría interpretar como algo distinto de lo que deseamos. Desde aquí, podría pasar muchos indicadores link diferentes. A los efectos de este tutorial, usaremos el indicador -X para escribir información en la variable en el tiempo de enlace, seguido de la ruta del paquete a la variable y su nuevo valor:

  • go build -ldflags="-X 'package_path.variable_name=new_value'"

Dentro de las comillas, ahora se encuentran la opción -X y un par clave-valor que representa la variable que se debe cambiar y su nuevo valor. El carácter . separa la ruta del paquete y el nombre de la variable, y se utilizan comillas simples para evitar la ruptura de los caracteres en el par clave-valor.

Para sustituir la variable Version en su aplicación de ejemplo, utilice la sintaxis del último bloque de comandos a fin de establecer un nuevo valor y compilar el nuevo binario:

  • go build -ldflags="-X 'main.Version=v1.0.0'"

En este comando, main es la ruta del paquete de la variable Version, dado que esta variable se encuentra en el archivo main.go. Version es la variable en la que escribe y v1.0.0 es el nuevo valor.

Para usar ldflags, el valor que desea cambiar debe existir y ser una variable de nivel de paquetes de tipo string. Esta variable puede ser exportada o no exportada. El valor no puede ser const y el resultado de la invocación de una función no puede fijarlo. Afortunadamente, Version cumple todos estos requisitos: ya se declaró como variable en el archivo main.go, y tanto el valor actual (development) como el valor deseado (v1.0.0) son cadenas.

Una vez compilado su nuevo binario app, ejecute la aplicación:

  • ./app

Recibirá el siguiente resultado:

Output
  • Version: v1.0.0

Con -ldflags, cambió de forma exitosa la variable Version de development a v1.0.0.

De esta manera, modificó una variable string dentro de una aplicación simple en el tiempo de compilación. Con ldflags, puede insertar detalles de versión, información de licenciamiento y otros datos en un binario listo para la distribución usando solo la línea de comandos.

En este ejemplo, la variable que cambió se encontraba en el programa main, lo que redujo la dificultad de determinar el nombre de la ruta. Sin embargo, a veces la ruta hacia estas variables es más difícil de encontrar. En el siguiente paso, escribirá valores en variables dentro de subpaquetes para demostrar la mejor manera de determinar rutas de paquetes más complejas.

Proporcionar orientación hacia variables de subpaquetes

En la última sección, manipuló la variable Version que se encontraba en el paquete de nivel superior de la aplicación. Sin embargo, esto no siempre es así. Suele ser más práctico disponer estas variables en otro paquete, ya que el paquete main no se puede importar. Para simular esto en su aplicación de muestra, creará un nuevo subpaquete, app/build, que almacenará información sobre el momento en que se compiló el binario y el nombre del usuario que ejecutó el comando de compilación.

Para añadir un nuevo subpaquete, primero agregue a su proyecto un nuevo directorio denominado build:

  • mkdir -p build

A continuación, cree un nuevo archivo llamado build.go para contener las variables nuevas:

  • nano build/build.go

En su editor de texto, añada nuevas variables para Time y User:

app/build/build.go
package build

var Time string

var User string

La variable Time contendrá una representación de cadena del momento en que se compiló el binario. La variable User contendrá el nombre del usuario que compiló el binario. Dado que estas dos variables siempre tendrán valores, no es necesario inicializarlas con valores predeterminados, como lo hizo para Version.

Guarde el archivo y ciérrelo.

A continuación, abra main.go para añadir estas variables a su aplicación:

  • nano main.go

Dentro de main.go, agregue las siguientes líneas resaltadas:

main.go
package main

import (
    "app/build"
    "fmt"
)

var Version = "development"

func main() {
    fmt.Println("Version:\t", Version)
    fmt.Println("build.Time:\t", build.Time)
    fmt.Println("build.User:\t", build.User)
}

En estas líneas, primero importó el paquete app/build y luego imprimió build.Time y build.User de la misma manera en que imprimió Version.

Guarde el archivo y cierre su editor de texto.

A continuación, para brindar orientación a estas variables con ldflags podría usar la ruta de importación app/build seguida de .User o .Time, dado que ya conoce la ruta de importación. Sin embargo, para simular una situación más compleja en la cual la ruta hacia la variable no sea tan evidente, usaremos el comando nm de la cadena de herramientas de Go.

Con el comando go tool nm se mostrarán los símbolos presentes en un ejecutable, un objeto o un archivo. En este caso, “símbolo” se refiere a un objeto en el código, como una variable o función definida o importada. Generando una tabla de símbolos con nm y usando grep para buscar una variable, puede obtener rápidamente información sobre su ruta.

Nota: El comando nm no lo ayudará a encontrar la ruta de su variable si el nombre del paquete tiene caracteres no ASCII, o bien los caracteres " o %, ya que es una limitación de la herramienta.

Para usar este comando, primero, compile el binario para app:

  • go build

Ahora que se compiló app, oriente la herramienta nm hacia ella y realice una búsqueda en el resultado:

  • go tool nm ./app | grep app

Cuando se ejecute, la herramienta nm mostrará muchos datos. Debido a esto, el comando anterior utilizó | para canalizar el resultado al comando grep, que luego buscó términos con la app de nivel superior en el título.

Recibirá un resultado similar a este:

Output
55d2c0 D app/build.Time 55d2d0 D app/build.User 4069a0 T runtime.appendIntStr 462580 T strconv.appendEscapedRune . . .

En este caso, las primeras dos líneas del resultado establecido contienen las rutas de las dos variables que busca: app/build.Time y app/build.Time.

Ahora que conoce las rutas, compile la aplicación de nuevo, pero, esta vez, cambie Version, User y Time en el tiempo de compilación. Para hacerlo, pase varios indicadores -X a -ldflags:

  • go build -v -ldflags="-X 'main.Version=v1.0.0' -X 'app/build.User=$(id -u -n)' -X 'app/build.Time=$(date)'"

Aquí, pasó el comando Bash id -u -n para mostrar el usuario actual y el comando date para mostrar la fecha actual.

Una vez compilado el ejecutable, ejecute el programa:

  • ./app

Este comando, cuando se ejecuta en un sistema Unix, genera un resultado similar al siguiente:

Output
Version: v1.0.0 build.Time: Fri Oct 4 19:49:19 UTC 2019 build.User: sammy

Ahora, dispondrá de un binario que contiene información de control de versiones y de compilación, y que puede proporcionar ayuda fundamental para la producción a la hora de resolver problemas.

Conclusión

En este tutorial, se demostró que ldflags, cuando se aplica correctamente, puede ser una potente herramienta para introducir información valiosa en binarios en el tiempo de compilación. De esta manera, puede controlar indicadores de características, información de entorno, información de control de versiones y otros elementos sin introducir cambios en su código fuente. Agregando ldflags a su flujo de trabajo de compilación actual, puede maximizar los beneficios del formato de distribución binaria autónoma de Go.

Si desea obtener más información acerca del lenguaje de programación Go, consulte toda la serie Cómo programar en Go. Si busca más soluciones para el control de versiones, consulte nuestra guía de referencia Cómo usar Git.

0 Comments

Creative Commons License