2010-07-27 19 views
6

un ejemplo para ilustrar mi pregunta:En un archivo MAKE, ¿cómo obtener la ruta relativa de una ruta absoluta a otra?

Makefile superior

rootdir = $(realpath .) 
export includedir = $(rootdir)/include 
default: 
    @$(MAKE) --directory=$(rootdir)/src/libs/libfoo 

Makefile para src/libtal

currentdir = $(realpath .) 
includedir = $(function or magic to make a relative path 
       from $(currentdir) to $(includedir), 
       which in this example would be ../../../include) 

Otro ejemplo:

current dir = /home/username/projects/app/trunk/src/libs/libfoo/ 
destination = /home/username/projects/app/build/libfoo/ 
relative = ../../../../build/libfoo 

¿Cómo se puede hacer esto , al tratar de ser lo más portátil posible?

Respuesta

6

Hacer lo que quiere no parece fácil. Es posible usar una gran cantidad de $(if combinados en el archivo MAKE pero no son portátiles (GMAKE solamente) y engorroso.

En mi humilde opinión, usted está tratando de resolver un problema que usted mismo crea. ¿Por qué no envía el valor correcto de includedir como una ruta relativa desde el Makefile de nivel superior? Se puede hacer muy fácilmente de la siguiente manera:

rootdir = $(realpath .) 
default: 
    @$(MAKE) --directory=$(rootdir)/src/libs/libfoo includedir=../../../include 

continuación, puede utilizar $(includedir) en los sub-archivos make. Ya está definido como relativo.

+0

Este meta-respuesta es el mejor - evitar el problema! Tenga en cuenta también que '--directory' no es portable:' cd $ (rootdir)/src/libs/libfoo; $ (MAKE) ... 'sería más confiable. –

+1

@Norman Gray: ¿qué pasa con '$ (MAKE) -C ...'? – Beta

+0

No creo que vaya tan lejos como _wrong_, pero no todos los comandos 'make' lo soportan (y no está en [posix] (http://www.opengroup.org/onlinepubs/009695399/utilities/make) .html), por ejemplo), por lo que si este archivo MAKE es para distribución, entonces podría crear problemas. En cualquier caso, tiendo a pensar que 'cd foo; $ (MAKE) ... 'expresa la intención más claramente. –

4

¡Python es portátil! Por lo tanto, me gustaría sugerir que este sencillo ejemplo en su submakefile

Con current_dir y destination_dir caminos os.path.relpath() hace el trabajo por usted para que no tenga que volver a inventar la rueda.

submakefile.mk

current_dir=$(CURDIR) 
makefile_target: 
    (echo "import os"; echo "print os.path.relpath('$(destination_dir)', '$(current_dir)')")| python 
+2

Primero: ¡gran solución! Tendré que acordarme de utilizar la impresionante biblioteca de SO de Python ... Segundo: cambiaste los argumentos de os.path.relpath(). Debería ser: os.path.relpath ('$ (destination_dir)', '$ (current_dir)') – jvriesem

+0

Escribí un script de python que tomaba 1-2 parámetros e imprimía el resultado. Luego llámalo por '$ (shell python relpath.py $ (destination_dir))'. Consejo increíble! –

2

respuesta de Didier es la mejor, pero lo siguiente podría darle algunas ideas:

includedir=/a/b/c/d 
currentdir=/a/b/e/f/g 
up=; while ! expr $includedir : $currentdir >/dev/null; do up=../$up; currentdir=`dirname $currentdir`; done; relative=$up`expr $includedir : $currentdir'/*\(.*\)'` 
echo "up=$up currentdir=$currentdir, relative=$relative" 

Ordenado!

(nadie dijo que tenía que ser bastante ...)

1

Aquí es una solución que única utiliza GNU make funciones. Aunque es recursivo, debería ser más eficiente que llamar a un programa externo. La idea es bastante directa: la ruta relativa será cero o más ... para subir al ancestro más común, luego un sufijo para bajar al segundo directorio. La parte difícil es encontrar el prefijo común más largo en ambas rutas.

# DOES not work if path has spaces 
OneDirectoryUp=$(patsubst %/$(lastword $(subst /, ,$(1))),%,$(1)) 

# FindParentDir2(dir0, dir1, prefix) returns prefix if dir0 and dir1 
# start with prefix, otherwise returns 
# FindParentDir2(dir0, dir1, OneDirectoryUp(prefix)) 
FindParentDir2= 
$(if 
    $(or 
    $(patsubst $(3)/%,,$(1)), 
    $(patsubst $(3)/%,,$(2)) 
    ), 
    $(call FindParentDir2,$(1),$(2),$(call OneDirectoryUp,$(3))), 
    $(3) 
) 

FindParentDir=$(call FindParentDir2,$(1),$(2),$(1)) 

# how to make a variable with a space, courtesy of John Graham-Cumming 
# http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html 
space:= 
space+= 

# dir1 relative to dir2 (dir1 and dir2 must be absolute paths) 
RelativePath=$(subst 
       $(space), 
       , 
       $(patsubst 
       %, 
       ../, 
       $(subst 
        /, 
        , 
        $(patsubst 
        $(call FindParentDir,$(1),$(2))/%, 
        %, 
        $(2) 
        ) 
       ) 
       ) 
      ) 
      $(patsubst 
       $(call FindParentDir,$(1),$(2))/%, 
       %, 
       $(1) 
      ) 

# example of how to use (will give ..) 
$(call RelativePath,/home/yale,/home/yale/workspace) 

Recientemente he traducido un gran conjunto de archivos make recursivas en un proyecto conjunto hacen, ya que es bien sabido que marca recursiva es mala debido a no exponer todo el grafo de dependencias (http://aegis.sourceforge.net/auug97.pdf). Todo el código fuente y las rutas de la biblioteca se definen en relación con el directorio actual del archivo MAKE. En lugar de definir un número fijo de% de reglas genéricas de compilación, creo un conjunto de reglas para cada par (directorio de código fuente, directorio de salida), lo que evita la ambigüedad de usar vpath. Al crear las reglas de compilación, necesito una ruta canónica para cada directorio de código fuente. Aunque se puede usar la ruta absoluta, suele ser demasiado larga y menos portátil (he utilizado Cygwin GNU make donde las rutas absolutas tienen un prefijo/cygdrive y no son reconocidas por los programas de Windows).Por lo tanto, utilizo esta función en gran medida para generar rutas canónicas.

4

La cáscara tiene esta función mediante realpath (1) y la --relative a bandera por lo que sólo podría invocar la cáscara.

Aquí se muestra un ejemplo:

RELATIVE_FILE1_FILE2:=$(shell realpath --relative-to $(FILE1) $(FILE2))

Incluso puede procesar toda una lista de archivos con una sola invocación de realpath (1) ya que sabe cómo procesar muchos nombres de archivo.

Aquí se muestra un ejemplo:

RELATIVES:=$(shell realpath --relative-to $(RELATIVE) $(FILES))
+0

realpath no existe en mi shell. (BSD) No creo que exista bajo cygwin, tampoco. – dbn

+1

realpath (1) es una parte de coreutils de GNU, por lo que es estándar en Linux. –

Cuestiones relacionadas