注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

如何让make使用特定shell  

2012-01-19 22:29:03|  分类: make源代码分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、定制SHELL
这个问题有windows下make使用经历的人都知道,可以通过SHELL或者MAKESHELL变量来让make使用某个特定的shell。例如你对sh情有独钟,那你就可以通过SHELL=D:\The\Path\To\Your\sh.exe来让make使用你的这个shell来执行和派生命令脚本。但是如果想把这个结论直接推到Linux下使用,那谢谢您嘞,不行!撒泼撒娇都不行(弱弱的说一句,我个人觉得,在make3.81版本中是不行的,如果万一是行的,那大家表拍砖)。
为什么这么说呢?因为我看源码而且做实验了,更重要的是最后我还把这个问题解决了。不信大家可以把自己的/bin/sh重命名一下,就是让/bin/sh这个文件不存在,然后执行make命令,看看能不能行,为了照顾那些不屑一试的同学,我这里代劳一下,非常简单的测试:
[root@Harry make-3.81]# cat test.mak   脚本内容很简单,就是打印一下用户根目录
default:
    cd ~;pwd
[root@Harry make-3.81]# ls /bin/s?  可以看到我把/bin/sh重命名了
/bin/su
[root@Harry make-3.81]# echo $SHELL   环境变量里是有SHELL变量的,事实上这个是一个Linux的标准环境变量。
/bin/bash
[root@Harry make-3.81]# make -f  test.mak
cd ~;pwd
make: /bin/sh: Command not found
make: *** [default] Error 127   这里就悲剧了,虽然环境变量里有SHELL变量,但是想执行make脚本,没门。
[root@Harry make-3.81]# make -f  test.mak SHELL=./sh.bak  直接在命令行里指定SHELL就可以
cd ~;pwd
/root
[root@Harry make-3.81]#
二、面临的问题及修改方法
当然,作为学院派的分析,前面已经算是找到解决方法了。但是事实上在做工程的时候没这么随意,因为这些你想改的代码不是你维护的,而且不是你一个人在用,不能你说修改就修改,你说真迹就真迹。如果想在别人的makefile中加上一个选项,那要通知很多人、很多地方来修改保持一致性,这时候撒娇没有用,撒泼也不行,你立马成了一个“受”。我们的想法就是要尽量减少影响,封装变化,将修改限制在可控范围内。所以要另外想招。
make中对于SHELL变量的定义为variable.c:define_automatic_variables (void),里面有一段长长的关于这个SHELL的设置代码:
#ifdef  __MSDOS__                                  这个是在windows下才会执行的代码路径,修改需要在Linux下同样打开这个宏
  /* Allow to specify a special shell just for Make,
     and use $COMSPEC as the default $SHELL when appropriate.  */
  {
    static char shell_str[] = "SHELL";
    const int shlen = sizeof (shell_str) - 1;
    struct variable *mshp = lookup_variable ("MAKESHELL", 9);
    struct variable *comp = lookup_variable ("COMSPEC", 7);

    /* Make $MAKESHELL override $SHELL even if -e is in effect.  */
    if (mshp)
      (void) define_variable (shell_str, shlen,
                  mshp->value, o_env_override, 0);
    else if (comp)
      {
    /* $COMSPEC shouldn't override $SHELL.  */
    struct variable *shp = lookup_variable (shell_str, shlen);

    if (!shp)
      (void) define_variable (shell_str, shlen, comp->value, o_env, 0);
      }
  }
……
  /* This won't override any definition, but it will provide one if there
     isn't one there.  */
  v = define_variable ("SHELL", 5, default_shell, o_default, 0);这里为了保险起见,在之前可能定义过SHELL(例如在windows下满足开始的__MSDOS__)之后又补了一枪,以最低优先级定义了SHELL为default_shell,这个default_shell在UNIX类系统中的值为"/bin/sh",这是一个确定路径,这也是make认死理儿的一个地方。可以看到,在Unix系统下,这个SHELL就会被定义为/bin./sh
……
  /* On MSDOS we do use SHELL from environment, since it isn't a standard
     environment variable on MSDOS, so whoever sets it, does that on purpose.
     On OS/2 we do not use SHELL from environment but we have already handled
     that problem above. */
#if !defined(__MSDOS__) && !defined(__EMX__)这里作者解释了为什么在Unix环境中不使用SHELL环境变量中定义的shell,例如在刚才的例子中,我的环境变量里的SHELL为/bin/bash,这个可执行文件是存在的,但是只要/bin/sh不存在,一样会执行失败
  /* Don't let SHELL come from the environment.  */
  if (*v->value == '\0' || v->origin == o_env || v->origin == o_env_override)
    {
      free (v->value);
      v->origin = o_file;
      v->value = xstrdup (default_shell);
    }
#endif

明显地,如果要让Linux下的make使用环境变量中的SHELL,就需要放开这里代码,将
#ifdef  __MSDOS__
修改为
#if 1
之后内容为
if (mshp)
      (void) define_variable (shell_str, shlen,
                  mshp->value, o_env_override, 0);这里修改为你期望的优先级,但是必须是比o_env_override优先级大,所以应该至少为o_command优先级,修改之后就可以通过MAKESHELL来在Linux下定义自己的SHELL了。为什么必须是在这个之上呢,因为在之后还有一个回马枪
  /* Don't let SHELL come from the environment.  */
  if (*v->value == '\0' || v->origin == o_env || v->origin == o_env_override)如果SHELL的优先级为o_env或者o_env_override(这个优先级正是make原始
    {                                                                                                               代码中使用的优先级),那么SHELL的定义还是会被default_shell强暴
      free (v->value);
      v->origin = o_file;
      v->value = xstrdup (default_shell);
    }
经过这样修改之后重新执行
[root@Harry make-3.81]# export MAKESHELL=`pwd`/sh.bak
[root@Harry make-3.81]# echo $MAKESHELL
/home/tsecer/Downloads/make-3.81/sh.bak
[root@Harry make-3.81]# ls /bin/s?
/bin/su
[root@Harry make-3.81]# ./make -f test.mak  可以在不存在/bin/sh的情况下执行成功
cd ~;pwd
/root
[root@Harry make-3.81]#
三、瞎扯
有人觉得,这样修改make的代码会不会很过分呢?其实还好,这个修改是可控修改,这个make没有必要安装到系统默认文件夹下,而是安装在一个我们自己使用的特殊文件夹,然后在调用make的时候在子进程中修改PATH变量,让订制的make在路径的最前端,从而可以使用这个新的make,而对于其它的传统方法,或者系统中其它的工程,这个修改不会影响到它们,所以这个修改是可控的。另一方面避免了其它makefile的大量修改,做到了修改对尽可能少的地方可见。
以前看网上的很多问题看起来很简单,大家也经常奇怪说为什么不这样这样,为什么有这么奇怪的需求。当在一个工程中修改的时候,就会知道,的确有各种奇奇怪怪的问题,看起来非常不按套路的方法反而是更好的解决方法。
  评论这张
 
阅读(781)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017