<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Bitácora de Vuelo (Publicaciones sobre compilar)</title><link>http://blog.taniquetil.com.ar/</link><description></description><atom:link href="http://blog.taniquetil.com.ar/categories/compilar.xml" rel="self" type="application/rss+xml"></atom:link><language>es</language><copyright>Contents © 2023 &lt;a href="mailto:facundo@taniquetil.com.ar"&gt;Facundo Batista&lt;/a&gt; CC BY-NC-SA</copyright><lastBuildDate>Mon, 29 May 2023 18:52:00 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Usando Go desde Python</title><link>http://blog.taniquetil.com.ar/posts/0748/</link><dc:creator>Facundo Batista</dc:creator><description>&lt;p&gt;¿Alguna vez necesitaron usar un código de &lt;a class="reference external" href="https://golang.org/"&gt;Go&lt;/a&gt; desde &lt;a class="reference external" href="http://python.org/"&gt;Python&lt;/a&gt;? Yo sí, y acá cuento qué hice.&lt;/p&gt;
&lt;p&gt;Antes que nada, un poco de background, para que el ejercicio no sea demasiado teórico: en el laburo tenemos que validar las licencias que se incluyen en el &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Snappy_(package_manager)"&gt;.snap&lt;/a&gt;, y aunque el formato en que están sería estándar (&lt;a class="reference external" href="https://spdx.org/licenses/"&gt;SPDX&lt;/a&gt;), una condición de contorno es usar el mismo parser/validador que se usa en snapd, para estar 107% seguros que el comportamiento va a ser el mismo hasta en los corner cases o bugs.&lt;/p&gt;
&lt;p&gt;El detalle es que &lt;a class="reference external" href="https://github.com/snapcore/snapd"&gt;snapd&lt;/a&gt; está escrito en Go, y el &lt;a class="reference external" href="https://launchpad.net/software-center-agent"&gt;server&lt;/a&gt; está escrito en Python. Entonces tengo que compilar ese código en Go y usarlo desde Python... de allí este post, claro.&lt;/p&gt;
&lt;p&gt;Es más fácil de lo que parece, ya que el compilador de Go tiene la capacidad de buildear a "biblioteca compartida", y de ahí usarlo desde Python es casi trivial ("casi", porque tenemos que poner algo de código en C).&lt;/p&gt;
&lt;p&gt;Para ser más claro, si queremos ejecutar "la lib de SPDX hecha en Go" desde nuestro Python, tenemos que poner dos componentes, que funcionan casi de adaptadores:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Un pequeño código en C para armar "como módulo" una funcioncita que recibe y entrega objetos Python, y hace la traducción al "mundo C" y llama a otra función en Go.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Un pequeño código en Go que traduce los parámetros desde C y llama a la biblioteca SPDX correspondiente.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="adaptador-de-python-a-c"&gt;
&lt;h2&gt;Adaptador de Python a C&lt;/h2&gt;
&lt;p&gt;El archivo completo es &lt;a class="reference external" href="http://www.taniquetil.com.ar/bdvfiles/code/pygo/spdx.c"&gt;spdx.c&lt;/a&gt;, paso a explicarlo haciendo antes la aclaración que es para Python 2 (que es lo que tenemos hoy en ese servicio), pero si fuera para Python 3 sería muy parecido (la idea es la misma, cambian algunos nombres, &lt;a class="reference external" href="https://docs.python.org/3/extending/extending.html"&gt;revisen acá&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Antes que nada, incluir la lib de Python:&lt;/p&gt;
&lt;pre class="literal-block"&gt;#include &amp;lt;Python.h&amp;gt;&lt;/pre&gt;
&lt;p&gt;Vamos a llamar a una función de Go, necesitamos explicitar lo que va recibir (una cadena de bytes, que a nivel de C es un puntero a chars)  y lo que nos devuelve (un número, que interpretaremos como bool):&lt;/p&gt;
&lt;pre class="literal-block"&gt;long IsValid(char *);&lt;/pre&gt;
&lt;p&gt;Definimos la función que vamos a llamar desde Python... es sencilla porque es genérica, recibe self y argumentos, devuelve un objeto Python:&lt;/p&gt;
&lt;pre class="literal-block"&gt;static PyObject *
is_valid(PyObject *self, PyObject *args)&lt;/pre&gt;
&lt;p&gt;El cuerpo de la función es sencillo también. Primero definimos 'source' (el string con la licencia a validar) y 'res' (el resultado), luego llamamos a &lt;code class="docutils literal"&gt;PyArg_ParseTuple&lt;/code&gt; que nos va a parsear 'args', buscando una cadena ('s') la cual va a poner en 'source' (y si algo sale mal nos vamos enseguida, para eso está el 'if' alrededor):&lt;/p&gt;
&lt;pre class="literal-block"&gt;{
    char * source;
    long res;

    if (!PyArg_ParseTuple(args, "s", &amp;amp;source))
        return NULL;&lt;/pre&gt;
&lt;p&gt;Finalmente llamamos a &lt;code class="docutils literal"&gt;IsValid&lt;/code&gt; (la función en Go), y a ese resultado lo convertimos en un objeto de Python tipo &lt;code class="docutils literal"&gt;bool&lt;/code&gt;, que es lo que realmente devolvemos:&lt;/p&gt;
&lt;pre class="literal-block"&gt;    res = IsValid(source);
    return PyBool_FromLong(res);
}&lt;/pre&gt;
&lt;p&gt;Ahora que tenemos nuestra función útil, debemos meterla en un módulo, para lo cual tenemos que definir qué cosas van a haber en dicho módulo. Entonces, armamos la siguiente estructura, con dos lineas; la primera habla sobre nuestra función, la última es una marca en la estructura para que sepa que es el final:&lt;/p&gt;
&lt;pre class="literal-block"&gt;static PyMethodDef SPDXMethods[] = {
    {"is_valid", is_valid, METH_VARARGS, "Check if the given license is valid."},
    {NULL, NULL, 0, NULL}
};&lt;/pre&gt;
&lt;p&gt;En la linea útil tenemos:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;"is_valid": es el nombre de la función que vamos a usar desde afuera del módulo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;is_valid: es una referencia a la función que tenemos definida arriba (para que sepa qué ejecutar cuando llamamos a "is_valid" desde afuera del módulo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;METH_VARARGS: la forma en que recibe los argumentos (fuertemente atado a como luego los parseamos con PyArg_ParseTuple arriba.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Check ...": el docstring de la función.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Para terminar con este código, va el inicializador del módulo, con un nombre predeterminado ("init" + nombre del módulo), y la inicialización propiamente dicha, pasando el nombre del módulo y la estructura que acabamos de definir arriba:&lt;/p&gt;
&lt;pre class="literal-block"&gt;PyMODINIT_FUNC
initspdx(void)
{
    (void) Py_InitModule("spdx", SPDXMethods);
}&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="adaptador-de-c-a-go"&gt;
&lt;h2&gt;Adaptador de C a Go&lt;/h2&gt;
&lt;p&gt;El archivo completo es &lt;a class="reference external" href="http://www.taniquetil.com.ar/bdvfiles/code/pygo/spdxlib.go"&gt;spdxlib.go&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Tenemos que meter el código en un paquete 'main':&lt;/p&gt;
&lt;pre class="literal-block"&gt;package main&lt;/pre&gt;
&lt;p&gt;Importamos el código para SPDX de snapd (tienen que bajarlo antes con &lt;code class="docutils literal"&gt;go get github.com/snapcore/snapd/spdx&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="literal-block"&gt;import "github.com/snapcore/snapd/spdx"&lt;/pre&gt;
&lt;p&gt;Importamos adaptadores desde/a C, indicando que cuando buildeemos vamos a usarlo desde Python 2:&lt;/p&gt;
&lt;pre class="literal-block"&gt;// #cgo pkg-config: python2
import "C"&lt;/pre&gt;
&lt;p&gt;La función propiamente dicha, donde indicamos que recibimos un puntero a char de C y vamos a devolver un bool:&lt;/p&gt;
&lt;pre class="literal-block"&gt;//export IsValid
func IsValid(license *C.char) bool {&lt;/pre&gt;
&lt;p&gt;El cuerpo es nuevamente sencillo: llamamos al ValidateLicense de SPDX (convirtiendo primero la cadena a Go), y luego comparamos el resultado para saber si la licencia es válida o no:&lt;/p&gt;
&lt;pre class="literal-block"&gt;    res := spdx.ValidateLicense(C.GoString(license))
    if res == nil {
        return true
    } else {
        return false
    }
}&lt;/pre&gt;
&lt;p&gt;Cerramos con la definición obligatoria de main:&lt;/p&gt;
&lt;pre class="literal-block"&gt;func main() {}&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="lo-usamos"&gt;
&lt;h2&gt;Lo usamos&lt;/h2&gt;
&lt;p&gt;Primer paso, buildear (yo tengo Go 1.6, creo que necesitan 1.5 o superior para poder armar directamente la biblioteca compartida de C, pero no estoy seguro):&lt;/p&gt;
&lt;pre class="literal-block"&gt;go build -buildmode=c-shared -o spdx.so&lt;/pre&gt;
&lt;p&gt;Segundo paso, profit!&lt;/p&gt;
&lt;pre class="literal-block"&gt;$ python2
&amp;gt;&amp;gt;&amp;gt; import spdx
&amp;gt;&amp;gt;&amp;gt; spdx.is_valid("GPL-3.0")
True&lt;/pre&gt;
&lt;/section&gt;</description><category>compilar</category><category>módulo</category><guid>http://blog.taniquetil.com.ar/posts/0748/</guid><pubDate>Mon, 09 Oct 2017 21:20:23 GMT</pubDate></item><item><title>Tipográfica y ffmpeg</title><link>http://blog.taniquetil.com.ar/posts/0317/</link><dc:creator>Facundo Batista</dc:creator><description>&lt;p&gt;El otro día, por una remera en la que mi hermana imprimió un grabado de su autoría, fuimos con ella, Moni y Gus a la imprenta de este último.&lt;/p&gt;
&lt;p&gt;Curioseamos bastante, y entre todo lo que Gustavo mostró, estaba su impresora tipográfica automática. Ya no la usa mucho, pero la prendió y la hizo funcionar para mostrarnos cómo trabajaba. Aproveché y la filmé con el teléfono:&lt;/p&gt;
&lt;div class="youtube-video align-center"&gt;
&lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/aqx92MnK5sQ?rel=0&amp;amp;wmode=transparent" frameborder="0" allow="encrypted-media" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;p&gt;Pero no fue todo tan fácil. El video que generé con el fono, en la computadora no lo podía ver bien. Bah, se &lt;em&gt;veía&lt;/em&gt; bien, pero salía sin sonido. Yo lo quería pasar a un formato más estándar, justamente para subirlo a Google Videos.&lt;/p&gt;
&lt;p&gt;En todos los lugares que busqué, siempre recomendaban utilizar el &lt;a class="reference external" href="http://ffmpeg.mplayerhq.hu/"&gt;ffmpeg&lt;/a&gt;, así que lo instalé de la forma normal (con el Synaptic, haciendo cuatro clicks), pero tampoco entendía el audio.&lt;/p&gt;
&lt;p&gt;Seguí buscando, hasta que encontré que, para que reprodujera bien el formato 3gp, debía compilar el ffmpeg con las bibliotecas de AMR. No fue demasiado complicado, pero tampoco fue recontra sencillo, así que reproduzco los pasos acá, que fueron más que nada el producto de seguir tres o cuatro guias sueltas por ahí.&lt;/p&gt;
&lt;p&gt;Si arrancamos con un Ubuntu crudo, les va a faltar lo necesario para compilar (creo que con instalar el paquete &lt;em&gt;build-essential&lt;/em&gt; deberían tener todo lo necesario).&lt;/p&gt;
&lt;p&gt;Creamos y nos metemos en un directorio para todo este experimento:&lt;/p&gt;
&lt;pre class="literal-block"&gt;mkdir locoffmpeg
cd locoffmpeg&lt;/pre&gt;
&lt;p&gt;El proyecto ffmpeg no tiene liberaciones estables, sino que normalmente se utilizan directamente los archivos de desarrollo. Para que no tengan que hacer un checkout del repositorio, pueden utilizar el snapshot que se genera todas las noches; lo bajamos y descomprimimos:&lt;/p&gt;
&lt;pre class="literal-block"&gt;wget http://ffmpeg.mplayerhq.hu/ffmpeg-export-snapshot.tar.bz2
tar -jxf ffmpeg-export-snapshot.tar.bz2&lt;/pre&gt;
&lt;p&gt;Traemos las fuentes de la biblioteca para AMR Narrow Band, la descomprimimos, vamos a ese directorio, la configuramos, compilamos e instalamos:&lt;/p&gt;
&lt;pre class="literal-block"&gt;wget http://downloads.sherin.in/sources/amrnb-7.0.0.0.tar.bz2
tar -jxf amrnb-7.0.0.0.tar.bz2
cd amrnb-7.0.0.0/
./configure --prefix=/usr
make
sudo make install
cd ..&lt;/pre&gt;
&lt;p&gt;Exactamente lo mismo, pero para la AMR Wide Band:&lt;/p&gt;
&lt;pre class="literal-block"&gt;wget http://downloads.sherin.in/sources/amrwb-7.0.0.2.tar.bz2
tar -jxf amrwb-7.0.0.2.tar.bz2
cd amrwb-7.0.0.2
./configure --prefix=/usr
make
sudo make install
cd ..&lt;/pre&gt;
&lt;p&gt;Dos detalles.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;En ambos &lt;code class="docutils literal"&gt;make&lt;/code&gt;, el tipo baja un pequeño archivo de algún lado, así que tienen que estar conectados a internet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fíjense que el &lt;code class="docutils literal"&gt;make install&lt;/code&gt; tiene un "sudo" adelante; esto es porque necesitan permisos de root para instalar la biblioteca.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luego entramos al directorio donde tenemos el ffmpeg descomprimido (el * en la primer órden aquí es porque el nombre del directorio depende del día, hagan un &lt;code class="docutils literal"&gt;ls &lt;span class="pre"&gt;-l&lt;/span&gt;&lt;/code&gt; y lo van a ver).&lt;/p&gt;
&lt;pre class="literal-block"&gt;cd ffmpeg-export-*&lt;/pre&gt;
&lt;p&gt;Una vez adentro, configuramos el ffmpeg para que se instale en &lt;code class="docutils literal"&gt;/usr/local&lt;/code&gt; (con lo que si tienen otra versión de ffmpeg instalado no lo van a pisar), y le habilitamos threading y las dos bibliotecas que nos interesan:&lt;/p&gt;
&lt;pre class="literal-block"&gt;./configure --prefix=/usr/local --enable-pthreads --enable-libamr-nb --enable-libamr-wb
make
sudo make install&lt;/pre&gt;
&lt;p&gt;Ya tenemos el ffmpeg &lt;em&gt;alternativo&lt;/em&gt; que compilamos, entonces transformamos el video del fono a algo estándar (en este caso, el video del fono se llama MOV00001.3gp, y dejamos el video nuevo en video.avi).&lt;/p&gt;
&lt;pre class="literal-block"&gt;/usr/local/bin/ffmpeg -i MOV00001.3gp -acodec mp2 -ar 44100 -vcodec mpeg4 video.avi&lt;/pre&gt;
&lt;p&gt;¡Ya está! El paso final fue subirlo a &lt;a class="reference external" href="http://video.google.es/"&gt;Google Video&lt;/a&gt;, pero eso se lo dejo a ustedes.&lt;/p&gt;</description><category>AMR</category><category>compilar</category><category>ffmpeg</category><category>instalar</category><category>video</category><guid>http://blog.taniquetil.com.ar/posts/0317/</guid><pubDate>Mon, 24 Dec 2007 18:47:32 GMT</pubDate></item></channel></rss>