자바스크립트를 활성화 해주세요

내 첫 리눅스 커널 기여

 ·  ☕ 14 min read

리눅스 커널 소스에 첫 기여를 한 기념으로 어떻게 기여하게 되었는지 그 과정을 공유한다. 사실 코드를 수정한 것도 아니고, 고작 1줄에 불과하기때문에 그렇게 대단한 것은 아니라고 생각하지만, 그 기여를 위해 어떤 판단을 했고, 어떻게 자료 조사를 했는지에 대한 경험을 공유하고자 한다.

어떻게 기여할 만한 것을 찾았는가?

리눅스 커널에서는 관련 문서를 Documentation/ 에서 관리하고 있다. 보통 *.rst*.txt로 각 주제에 대한 글을 작성해 놓은 상태다.

물론 해당 문서 파일들을 더 효과적인 방식으로 변환하는 기능도 제공한다. 예를 들어 make pdfdocs, make htmldocs 등이 있다. 말 그대로 pdf문서로 변환해주거나, HTML로 변환하여 웹에서 제공할 수 있게 해준다.

현재 iamroot 커널 스터디를 진행하고 있는데, 추후 개념 관련 정리를 rst로 작성하여 웹으로 배포하면 좋겠다는 생각이 들어서 htmldocs로 변환하는 부분을 실험해봤다. 그런데 오류 메시지만으로는 해결할 수 없는 상태가 발생했고, 이를 수정하는 과정을 기여할 수 있었다.

커널의 make htmldocs

커널에서 문서를 HTML로 변환할 때 sphinx라는 도구를 사용한다.

$ make htmldocs
Documentation/Makefile:30: The 'sphinx-build' command was not found. Make sure you have Sphinx installed and in PATH, or set the SPHINXBUILD make variable to point to the full path of the 'sphinx-build' executable.

Detected OS: Ubuntu 20.04.1 LTS.
Warning: better to also install "convert".
Warning: better to also install "dot".
Warning: better to also install "dvipng".
Warning: better to also install "fonts-noto-cjk".
Warning: better to also install "latexmk".
Warning: better to also install "rsvg-convert".
You should run:

        sudo apt-get install imagemagick graphviz dvipng fonts-noto-cjk latexmk librsvg2-bin
Warning: It is recommended at least Sphinx version 1.7.9.
         If you want pdf, you need at least 2.4.4.
Note: It is recommended at least Sphinx version 2.4.4 if you need PDF support.
Can't build as 1 mandatory dependency is missing at ./scripts/sphinx-pre-install line 853.
        /usr/bin/python3 -m venv sphinx_2.4.4
        . sphinx_2.4.4/bin/activate
        pip install -r ./Documentation/sphinx/requirements.txt

If you want to exit the virtualenv, you can use:
        deactivate

make[1]: *** [Documentation/Makefile:32: htmldocs] Error 2
make: *** [Makefile:1658: htmldocs] Error 2

만약 sphinx가 설치되어 있지 않다면 위와 같이 에러문이 출력된다. 각 OS에 따라 sphinx, 기타 관련 추천 패키지를 설치하기 위한 명령을 알려준다. 참고로 sphinx는 python 패키지로 설치되기 때문에 효과적인 관리를 위해 virtualenv를 생성하고, 그 위에서 pip를 통해 문서 생성에 필요한 패키지를 자동으로 설치시킨다. 필요한 패키지는 requirements.txt 안에 정의되어 있다.

Sphinx 설치 후 재시도

위의 명령과 비슷하게 virtualenv를 설정하고 필요 패키지를 설치한 다음 다시 make htmldocs를 시도해보자. (나는 git 관리의 편의를 위해 virtualenv 디렉토리를 .sphinx로 변경했다.)

$ /usr/bin/python3 -m venv .sphinx
$ . .sphinx/bin/activate
$ pip install -r ./Documentation/sphinx/requirements.txt
Collecting docutils
  Downloading docutils-0.16-py2.py3-none-any.whl (548 kB)
     |████████████████████████████████| 548 kB 1.9 MB/s
Collecting Sphinx==2.4.4
  Downloading Sphinx-2.4.4-py3-none-any.whl (2.7 MB)
     |████████████████████████████████| 2.7 MB 11.2 MB/s
Collecting sphinx_rtd_theme
  Downloading sphinx_rtd_theme-0.5.0-py2.py3-none-any.whl (10.8 MB)
     |████████████████████████████████| 10.8 MB 11.6 MB/s
 : # (다른 설치 기록은 생략함)
Successfully installed Jinja2-2.11.2 MarkupSafe-1.1.1 Pygments-2.7.3 Sphinx-2.4.4 alabaster-0.7.12 babel-2.9.0 certifi-2020.12.5 chardet-3.0.4 docutils-0.16 idna-2.10 imagesize-1.2.0 packaging-20.7 pyparsing-2.4.7 pytz-2020.4 requests-2.25.0 snowballstemmer-2.0.0 sphinx-rtd-theme-0.5.0 sphinxcontrib-applehelp-1.0.2 sphinxcontrib-devhelp-1.0.2 sphinxcontrib-htmlhelp-1.0.3 sphinxcontrib-jsmath-1.0.1 sphinxcontrib-qthelp-1.0.3 sphinxcontrib-serializinghtml-1.1.4 urllib3-1.26.2
$ make htmldocs
  SPHINX  htmldocs --> file:///home/jsyoo5b/Workspace/Kernel/iamroot/Documentation/output
  PARSE   include/uapi/linux/dvb/audio.h
  PARSE   include/uapi/linux/dvb/ca.h
  PARSE   include/uapi/linux/dvb/dmx.h
  PARSE   include/uapi/linux/dvb/frontend.h
  PARSE   include/uapi/linux/dvb/net.h
  PARSE   include/uapi/linux/dvb/video.h
  PARSE   include/uapi/linux/videodev2.h
  PARSE   include/uapi/linux/media.h
  PARSE   include/uapi/linux/cec.h
  PARSE   include/uapi/linux/lirc.h
Running Sphinx v2.4.4

Extension error:
Could not import extension kfigure (exception: No module named 'six')
enabling CJK for LaTeX builder
make[1]: *** [Documentation/Makefile:82: htmldocs] Error 2
make: *** [Makefile:1658: htmldocs] Error 2

문서를 생성하던 도중 에러가 발생한다. 하지만 어떻게 해결해야 하는지에 대한 해결법은 나오지 않는다. 이 부분이 내가 기여한 이슈의 시작점이다.

Sphinx 문서 생성의 문제점 분석

위의 make htmldocs에서 에러가 난 부분만 다시 집중해보자.

$ make htmldocs
  SPHINX  htmldocs --> file:///home/jsyoo5b/Workspace/Kernel/iamroot/Documentation/output
  PARSE   include/uapi/linux/dvb/audio.h
  PARSE   include/uapi/linux/dvb/ca.h
  PARSE   include/uapi/linux/dvb/dmx.h
  PARSE   include/uapi/linux/dvb/frontend.h
  PARSE   include/uapi/linux/dvb/net.h
  PARSE   include/uapi/linux/dvb/video.h
  PARSE   include/uapi/linux/videodev2.h
  PARSE   include/uapi/linux/media.h
  PARSE   include/uapi/linux/cec.h
  PARSE   include/uapi/linux/lirc.h
Running Sphinx v2.4.4

Extension error:
Could not import extension kfigure (exception: No module named 'six')
enabling CJK for LaTeX builder
make[1]: *** [Documentation/Makefile:82: htmldocs] Error 2
make: *** [Makefile:1658: htmldocs] Error 2

kfigure라는 extension을 import할수 없는데, 그 이유는 six란 모듈을 찾을수 없기 때문이라고 한다. 일단 kfigure가 뭔지 확인해보자.

$ ls Documentation/sphinx/
automarkup.py  kernel_include.py  load_config.py          parse-headers.pl
cdomain.py     kernellog.py       maintainers_include.py  requirements.txt
kerneldoc.py   kfigure.py         parallel-wrapper.sh     rstFlatTable.py

kfigure는 Documentation/sphinx/에 작성된 모듈로, rst에서 그린 그림을 이미지로 변환해주는 모듈이다. 그럼 해당 파일의 내용에서 six가 사용되는 부분을 확인해보자.

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from docutils.parsers.rst.directives import images
import sphinx

from sphinx.util.nodes import clean_astext
from six import iteritems

import kernellog

PY3 = sys.version_info[0] == 3

if PY3:
    _unicode = str
else:
    _unicode = unicode

# Get Sphinx version
major, minor, patch = sphinx.version_info[:3]
if major == 1 and minor > 3:
    # patches.Figure only landed in Sphinx 1.4
    from sphinx.directives.patches import Figure  # pylint: disable=C0413
else:
    Figure = images.Figure

__version__  = '1.0.0'
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
def add_kernel_figure_to_std_domain(app, doctree):
    """Add kernel-figure anchors to 'std' domain.

    The ``StandardDomain.process_doc(..)`` method does not know how to resolve
    the caption (label) of ``kernel-figure`` directive (it only knows about
    standard nodes, e.g. table, figure etc.). Without any additional handling
    this will result in a 'undefined label' for kernel-figures.

    This handle adds labels of kernel-figure to the 'std' domain labels.
    """

    std = app.env.domains["std"]
    docname = app.env.docname
    labels = std.data["labels"]

    for name, explicit in iteritems(doctree.nametypes):
        if not explicit:
            continue
        labelid = doctree.nameids[name]
        if labelid is None:
            continue
        node = doctree.ids[labelid]

        if node.tagname == 'kernel_figure':
            for n in node.next_node():
                if n.tagname == 'caption':
                    sectname = clean_astext(n)
                    # add label to std domain
                    labels[name] = docname, labelid, sectname
                    break

확인해보니 six에서 iteritem 함수를 사용하는데, 현재 virtualenv에서 설치한 패키지 중에는 six가 존재하지 않는 것 같다.

문제의 six 패키지에 대한 정보를 검색해보니 python2와 python3 사이의 호환성을 유지하기 위해 사용하는 모듈이라 한다. 그런데 왜 six는 자동으로 설치되지 않았을까?

requirements.txt 확인

일단 Sphinx를 실행시키는 환경의 requirements.txt의 내용을 확인해보자.

docutils
Sphinx==2.4.4
sphinx_rtd_theme

설치 목록에 six가 없는 것을 알 수 있다. 하지만 지금 증상을 다른 사람들이 눈치채지 못했다는 것은, 저기 목록에 있는 패키지 중에 하나가 원래는 six를 자동으로 설치했을 것이다.

이전 설치기록을 보면 알 수 있듯이, 위 3가지 패키지를 설치하기 위해 그 패키지들이 필요로 하는 하위 의존성 있는 패키지들을 같이 설치한 것을 볼 수 있다.

$ pip install -r ./Documentation/sphinx/requirements.txt
Collecting docutils
  Downloading docutils-0.16-py2.py3-none-any.whl (548 kB)
     |████████████████████████████████| 548 kB 1.9 MB/s
Collecting Sphinx==2.4.4
  Downloading Sphinx-2.4.4-py3-none-any.whl (2.7 MB)
     |████████████████████████████████| 2.7 MB 11.2 MB/s
Collecting sphinx_rtd_theme
  Downloading sphinx_rtd_theme-0.5.0-py2.py3-none-any.whl (10.8 MB)
     |████████████████████████████████| 10.8 MB 11.6 MB/s
Collecting Pygments>=2.0
  Downloading Pygments-2.7.3-py3-none-any.whl (950 kB)
     |████████████████████████████████| 950 kB 6.4 MB/s
Collecting babel!=2.0,>=1.3
  Downloading Babel-2.9.0-py2.py3-none-any.whl (8.8 MB)
     |████████████████████████████████| 8.8 MB 11.8 MB/s
Requirement already satisfied: setuptools in ./.sphinx/lib/python3.8/site-packages (from Sphinx==2.4.4->-r ./Documentation/sphinx/requirements.txt (line 2)) (44.0.0)
Collecting requests>=2.5.0
  Downloading requests-2.25.0-py2.py3-none-any.whl (61 kB)
     |████████████████████████████████| 61 kB 8.1 MB/s
Collecting sphinxcontrib-serializinghtml
  Downloading sphinxcontrib_serializinghtml-1.1.4-py2.py3-none-any.whl (89 kB)
     |████████████████████████████████| 89 kB 8.1 MB/s
Collecting sphinxcontrib-jsmath
  Downloading sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl (5.1 kB)
Collecting sphinxcontrib-htmlhelp
  Downloading sphinxcontrib_htmlhelp-1.0.3-py2.py3-none-any.whl (96 kB)
     |████████████████████████████████| 96 kB 6.3 MB/s
Collecting packaging
  Downloading packaging-20.7-py2.py3-none-any.whl (35 kB)
Collecting alabaster<0.8,>=0.7
  Downloading alabaster-0.7.12-py2.py3-none-any.whl (14 kB)
Collecting sphinxcontrib-devhelp
  Downloading sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl (84 kB)
     |████████████████████████████████| 84 kB 2.9 MB/s
Collecting sphinxcontrib-qthelp
  Downloading sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl (90 kB)
     |████████████████████████████████| 90 kB 8.9 MB/s
Collecting snowballstemmer>=1.1
  Downloading snowballstemmer-2.0.0-py2.py3-none-any.whl (97 kB)
     |████████████████████████████████| 97 kB 7.4 MB/s
Collecting imagesize
  Downloading imagesize-1.2.0-py2.py3-none-any.whl (4.8 kB)
Collecting Jinja2>=2.3
  Downloading Jinja2-2.11.2-py2.py3-none-any.whl (125 kB)
     |████████████████████████████████| 125 kB 12.3 MB/s
Collecting pytz>=2015.7
  Downloading pytz-2020.4-py2.py3-none-any.whl (509 kB)
     |████████████████████████████████| 509 kB 12.2 MB/s
Collecting chardet<4,>=3.0.2
  Downloading chardet-3.0.4-py2.py3-none-any.whl (133 kB)
     |████████████████████████████████| 133 kB 12.1 MB/s
Collecting idna<3,>=2.5
  Downloading idna-2.10-py2.py3-none-any.whl (58 kB)
     |████████████████████████████████| 58 kB 7.7 MB/s
Collecting urllib3<1.27,>=1.21.1
  Downloading urllib3-1.26.2-py2.py3-none-any.whl (136 kB)
     |████████████████████████████████| 136 kB 13.2 MB/s
Collecting certifi>=2017.4.17
  Downloading certifi-2020.12.5-py2.py3-none-any.whl (147 kB)
     |████████████████████████████████| 147 kB 12.4 MB/s
Collecting pyparsing>=2.0.2
  Downloading pyparsing-2.4.7-py2.py3-none-any.whl (67 kB)
     |████████████████████████████████| 67 kB 6.4 MB/s
Collecting MarkupSafe>=0.23
  Downloading MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl (32 kB)
Installing collected packages: docutils, sphinxcontrib-applehelp, Pygments, pytz, babel, chardet, idna, urllib3, certifi, requests, sphinxcontrib-serializinghtml, sphinxcontrib-jsmath, sphinxcontrib-htmlhelp, pyparsing, packaging, alabaster, sphinxcontrib-devhelp, sphinxcontrib-qthelp, snowballstemmer, imagesize, MarkupSafe, Jinja2, Sphinx, sphinx-rtd-theme
Successfully installed Jinja2-2.11.2 MarkupSafe-1.1.1 Pygments-2.7.3 Sphinx-2.4.4 alabaster-0.7.12 babel-2.9.0 certifi-2020.12.5 chardet-3.0.4 docutils-0.16 idna-2.10 imagesize-1.2.0 packaging-20.7 pyparsing-2.4.7 pytz-2020.4 requests-2.25.0 snowballstemmer-2.0.0 sphinx-rtd-theme-0.5.0 sphinxcontrib-applehelp-1.0.2 sphinxcontrib-devhelp-1.0.2 sphinxcontrib-htmlhelp-1.0.3 sphinxcontrib-jsmath-1.0.1 sphinxcontrib-qthelp-1.0.3 sphinxcontrib-serializinghtml-1.1.4 urllib3-1.26.2

하지만 위의 상황에서 설명했듯, six는 설치되지 않았다. 혹시 다른 패키지나 six가 설치 목록에 있었는데 삭제된 것은 아닌지 확인해보자.

$ git --no-pager log --stat Documentation/sphinx/requirements.txt
commit d5afc9640a6d4596e57a2c4906f903ab1c83ada5
Author: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Date:   Tue Apr 14 18:48:30 2020 +0200

    docs: update recommended Sphinx version to 2.4.4

    There are some docs that have nested tables. While this was
    always part of the spec, only Sphinx version 2.4.x can
    translate it to LaTeX.

    In other words, if someone is using a Sphinx version < 2.4,
    the LaTeX and PDF output won't work for some of the docs.

    So, it seems that it is time to raise the bar again
    for the recommented version.

    The Sphinx check script is already smart enough to keep
    working, with older versions, warning the users that
    an upgrade is recommended (and explaining how):

            Sphinx version 1.7.9
            Warning: It is recommended at least Sphinx version 2.4.4.
            Detected OS: Fedora release 31 (Thirty One).

            To upgrade Sphinx, use:

                    /usr/bin/virtualenv sphinx_2.4.4
                    . sphinx_2.4.4/bin/activate
                    pip install -r ./Documentation/sphinx/requirements.txt

    Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
    Link: https://lore.kernel.org/r/498f701c618f7d0cf5f0a37e5889ee926f7c8bf4.1586881715.git.mchehab+huawei@kernel.org
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

commit a700767a7682d9bd237e927253274859aee075e7
Author: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Date:   Wed May 29 20:09:32 2019 -0300

    docs: requirements.txt: recommend Sphinx 1.7.9

    As discussed at the linux-doc ML, while we'll still support
    version 1.3, it is time to recommend a more modern version.

    So, let's switch the minimal requirements to Sphinx 1.7.9,
    as it has the "-jauto" flag, with makes a lot faster when
    building documentation.

    Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/requirements.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

commit fb947f3f472303d54759bf898cf92fd8c2dc9bdf
Author: Mauro Carvalho Chehab <mchehab@kernel.org>
Date:   Mon Jul 17 18:46:38 2017 -0300

    sphinx-pre-install: use a requirements file

    Instead of using 3 commands to install a virtualenv, use
    a single one, reading the requirements from this file:

            Documentation/sphinx/requirements.txt

    Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/requirements.txt | 3 +++
 1 file changed, 3 insertions(+)

다행히 requirements.txt의 변화 기록도 많지 않고, 변화분도 적어서 쉽게 확인할 수 있다. 시간순으로 각 commit에 대해 요약하자면 아래와 같다.

  1. 원래는 virtualenv에서 각 패키지들 설치 명령을 직접 입력하던 것을 간소화(fb947f3f472303d54759bf898cf92fd8c2dc9bdf)
  2. 문서 생성 속도를 빠르게 하기 위해 Sphinx의 버전을 1.7.9로 변경함(a700767a7682d9bd237e927253274859aee075e7)
  3. 다른 문서 생성하는 부분에 Sphinx 1.7.9는 부적합해서 버전을 2.4.4로 변경함(d5afc9640a6d4596e57a2c4906f903ab1c83ada5)

딱히 six가 지워질 이유는 없었던 것 같다. 그렇다면 혹시 six를 사용하는 kfigure.py에서 갑자기 six를 사용하기 시작했는데 requirements.txt에 추가하는 것을 빼먹었을 수도 있지 않을까? kfigure.py의 수정 기록을 찾아보자.

kfigure.py에서 six 부분 기록 확인하기

$ git --no-pager log --stat Documentation/sphinx/kfigure.py
commit 93431e0607e58a3c997a134adc0fad4fdc147dab
Author: Alexander A. Klimov <grandmaster@al2klimov.de>
Date:   Tue May 26 08:05:44 2020 +0200

    Replace HTTP links with HTTPS ones: documentation

    Rationale:
    Reduces attack surface on kernel devs opening the links for MITM
    as HTTPS traffic is much harder to manipulate.

    Deterministic algorithm:
    For each file:
      For each line:
        If doesn't contain `\bxmlns\b`:
          For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`:
            If both the HTTP and HTTPS versions
            return 200 OK and serve the same content:
              Replace HTTP with HTTPS.

    Signed-off-by: Alexander A. Klimov <grandmaster@al2klimov.de>
    Link: https://lore.kernel.org/r/20200526060544.25127-1-grandmaster@al2klimov.de
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/kfigure.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

commit 096ea522e84ea68f8e6c41e5e7294731a81e29bc
Author: Jonathan Corbet <corbet@lwn.net>
Date:   Tue May 21 14:23:43 2019 -0600

    doc: Cope with Sphinx logging deprecations

    Recent versions of sphinx will emit messages like:

      Documentation/sphinx/kerneldoc.py:103:
         RemovedInSphinx20Warning: app.warning() is now deprecated.
         Use sphinx.util.logging instead.

    Switch to sphinx.util.logging to make this unsightly message go away.
    Alas, that interface was only added in version 1.6, so we have to add a
    version check to keep things working with older sphinxes.

    Cc: stable@vger.kernel.org
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/kfigure.py | 40 +++++++++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 17 deletions(-)

commit ae17a87dd7c79fa742ef5dcf06d1095eec4e1925
Author: Masanari Iida <standby24x7@gmail.com>
Date:   Thu Jan 11 20:00:28 2018 +0900

    linux-next: docs-rst: Fix typos in kfigure.py

    This patch fixes some spelling typos found in kfigure.py

    Signed-off-by: Masanari Iida <standby24x7@gmail.com>
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/kfigure.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

commit db6ccf23e8ba40fc2e8914ec9c0eb950df71d9fe
Author: Markus Heiser <markus.heiser@darmarit.de>
Date:   Mon Mar 6 14:09:27 2017 +0100

    docs-rst: automatically convert Graphviz and SVG images

    This patch brings scalable figure, image handling and a concept to
    embed *render* markups:

    * DOT (http://www.graphviz.org)
    * SVG

    For image handling use the 'image' replacement::

        .. kernel-image::  svg_image.svg
           :alt:    simple SVG image

    For figure handling use the 'figure' replacement::

        .. kernel-figure::  svg_image.svg
           :alt:    simple SVG image

           SVG image example

    Embed *render* markups (or languages) like Graphviz's **DOT** is
    provided by the *render* directive.::

      .. kernel-render:: DOT
         :alt: foobar digraph
         :caption: Embedded **DOT** (Graphviz) code.

         digraph foo {
          "bar" -> "baz";
         }

    The *render* directive is a concept to integrate *render* markups and
    languages, yet supported markups:

    * DOT: render embedded Graphviz's **DOT**
    * SVG: render embedded Scalable Vector Graphics (**SVG**)

    Cc: Jani Nikula <jani.nikula@linux.intel.com>
    Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
    Tested-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
    Tested-by: Daniel Vetter <daniel.vetter@ffwll.ch>
    Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> (v2 - v5)
    Signed-off-by: Markus Heiser <markus.heiser@darmarit.de> (v1, v6)
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/kfigure.py | 551 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 551 insertions(+)

commit 기록이 많은 것은 아니지만, 딱히 six를 언급하는 부분은 찾을 수 없다. 그렇다면 git log보단 git blame으로 해당 라인의 기록을 찾는 것이 더 효율적이다.

$ git --no-pager blame -L48,62 Documentation/sphinx/kfigure.py                    
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 48) import os
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 49) from os import path
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 50) import subprocess
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 51) from hashlib import sha1
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 52) import sys
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 53)
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 54) from docutils import nodes
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 55) from docutils.statemachine import ViewList
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 56) from docutils.parsers.rst import directives
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 57) from docutils.parsers.rst.directives import images
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 58) import sphinx
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 59)
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 60) from sphinx.util.nodes import clean_astext
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 61) from six import iteritems
db6ccf23e8ba4 (Markus Heiser 2017-03-06 14:09:27 +0100 62)

위에서 확인했던 commit 기록과 비교해봤을 때(db6ccf23e8ba40fc2e8914ec9c0eb950df71d9fe), six는 kfigure.py를 처음 작성했을 때 부터 계속 사용해 왔음을 알 수 있다. 그렇다면 다시 requirements.txt의 기록을 다시 찾아봐야 한다.

requirements.txt의 하위 의존성 패키지 찾아보기

지금까지 분석한 것을 보고 평소 python으로 개발을 해 본 사람으로 생각했다면, 아쉽게도 나는 python을 아주 가끔 쓰기 때문에 전문적으로 아는 것은 아니다. 각 패키지 설치의 하위 의존성을 확인하는 방법을 검색해봤는데 나는 찾지 못했다. (키워드를 잘 모르는 것일 가능성이 높아보임)

현재로선 requirements.txt에 작성된 패키지를 직접 찾아 들어가서 하위 호환성을 확인해 보는 수밖에 없겠다. (사실 3가지 commit 버전을 모두 requirements.txt로 설치해보고 언제부터 six가 설치 목록에서 사라졌는지 확인하는 것이 제일 쉽다.)

requirements.txt의 기록을 보면 Sphinx의 버전이 2번 변경되었고, docutils가 1번 변경되었다. 변경 횟수가 더 많은 Sphinx가 더 의심스러워서 Sphinx를 먼저 찾아봤다. Sphinx의 소스에서 하위 의존성 패키지 설치 목록이 정의된 파일은 setup.py인 것 같다. 코드의 install_requires 리스트에서 하위 의존성 패키지를 선언하고 있고, 제일 아래 setup의 인자로 전달되는 것을 확인할 수 있다.

다행히 requirements.txt에서 Sphinx의 버전이 명시되어 있으므로, 일단은 각 버전 별로 setup.py를 비교해보자. Sphinx는 각 버전을 배포할 때마다 버전 이름으로 tag를 달아놨으므로, tag를 기반으로 각 버전을 찾아가면 되겠다.

각 tag에 들어가면 tag가 연결된 commit id를 알아낼 수 있다. 해당 commit id를 클릭하면 그 commit 당시의 소스 코드를 확인할 수 있다. 각 버전, 해당 commit id, 그때 당시의 setup.py를 링크로 정리하면 다음과 같다.

태그commit id해당 파일 상태
v2.4.472ad5f2setup.py
v1.7.91cd87a1setup.py

분명 v1.7.9에는 install_requires에 six가 있지만, v2.4.4에는 존재하지 않는 것을 확인할 수 있다. 결국 Sphinx 버전이 올라가면서 six가 하위 의존성에서 제외되면서 자동으로 설치되지 않게 되었음을 알 수 있다.

참고로 six는 2.x부터 하위 의존성에서 제외된 것을 확인할 수 있다.

왜 이 문제를 발견하지 못했을까?

Sphinx 버전을 2.4.4로 올린 commit의 로그를 다시 확인해보자.

commit d5afc9640a6d4596e57a2c4906f903ab1c83ada5
Author: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Date:   Tue Apr 14 18:48:30 2020 +0200

    docs: update recommended Sphinx version to 2.4.4

    There are some docs that have nested tables. While this was
    always part of the spec, only Sphinx version 2.4.x can
    translate it to LaTeX.

    In other words, if someone is using a Sphinx version < 2.4,
    the LaTeX and PDF output won't work for some of the docs.

    So, it seems that it is time to raise the bar again
    for the recommented version.

    The Sphinx check script is already smart enough to keep
    working, with older versions, warning the users that
    an upgrade is recommended (and explaining how):

            Sphinx version 1.7.9
            Warning: It is recommended at least Sphinx version 2.4.4.
            Detected OS: Fedora release 31 (Thirty One).

            To upgrade Sphinx, use:

                    /usr/bin/virtualenv sphinx_2.4.4
                    . sphinx_2.4.4/bin/activate
                    pip install -r ./Documentation/sphinx/requirements.txt

    Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
    Link: https://lore.kernel.org/r/498f701c618f7d0cf5f0a37e5889ee926f7c8bf4.1586881715.git.mchehab+huawei@kernel.org
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

 Documentation/sphinx/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

여기에서는 Sphinx 검사 스크립트가 버전 업그레이드 필요 여부에 대한 경고 및 설치법을 잘 설명하고 있다고만 언급하고 있다.

즉, 해당 패치를 작성할 때는 Sphinx 버전을 업그레이드하는 경우만 고려하고, 처음부터 설치하는 경우는 고려하지 않은 상태였음을 알 수 있다. 1.7.9에서 2.4.4로 업그레이드하는 경우는 이미 1.7.9 설치 과정에서 six가 설치되어 있기 때문에 이런 문제를 확인하지 못한 것으로 보인다.

문제 해결 및 commit

일단 six가 없는 문제는 six를 설치하면 된다. 위에서 requirements.txt를 사용했으니 여기에 six를 추가하면 된다.

그리고 혹시 다른 모듈에서도 설치되지 않은 패키지가 있는지 확인해보자.

$ grep -r "import" Documentation/sphinx
Documentation/sphinx/automarkup.py:from docutils import nodes
Documentation/sphinx/automarkup.py:from sphinx import addnodes
Documentation/sphinx/automarkup.py:from sphinx.environment import NoUri
Documentation/sphinx/automarkup.py:import re
Documentation/sphinx/load_config.py:import os
Documentation/sphinx/load_config.py:import sys
Documentation/sphinx/load_config.py:from sphinx.util.pycompat import execfile_
Documentation/sphinx/kernel_include.py:# imports
Documentation/sphinx/kernel_include.py:import os.path
Documentation/sphinx/kernel_include.py:from docutils import io, nodes, statemachine
Documentation/sphinx/kernel_include.py:from docutils.utils.error_reporting import SafeString, ErrorString
Documentation/sphinx/kernel_include.py:from docutils.parsers.rst import directives
Documentation/sphinx/kernel_include.py:from docutils.parsers.rst.directives.body import CodeBlock, NumberLines
Documentation/sphinx/kernel_include.py:from docutils.parsers.rst.directives.misc import Include
Documentation/sphinx/kerneldoc.py:import codecs
Documentation/sphinx/kerneldoc.py:import os
Documentation/sphinx/kerneldoc.py:import subprocess
Documentation/sphinx/kerneldoc.py:import sys
Documentation/sphinx/kerneldoc.py:import re
Documentation/sphinx/kerneldoc.py:import glob
Documentation/sphinx/kerneldoc.py:from docutils import nodes, statemachine
Documentation/sphinx/kerneldoc.py:from docutils.statemachine import ViewList
Documentation/sphinx/kerneldoc.py:from docutils.parsers.rst import directives, Directive
Documentation/sphinx/kerneldoc.py:import sphinx
Documentation/sphinx/kerneldoc.py:    from sphinx.util.docutils import switch_source_input
Documentation/sphinx/kerneldoc.py:    from sphinx.ext.autodoc import AutodocReporter
Documentation/sphinx/kerneldoc.py:import kernellog
Documentation/sphinx/kernellog.py:import sphinx
Documentation/sphinx/kernellog.py:    from sphinx.util import logging
Documentation/sphinx/rstFlatTable.py:# imports
Documentation/sphinx/rstFlatTable.py:import sys
Documentation/sphinx/rstFlatTable.py:from docutils import nodes
Documentation/sphinx/rstFlatTable.py:from docutils.parsers.rst import directives, roles
Documentation/sphinx/rstFlatTable.py:from docutils.parsers.rst.directives.tables import Table
Documentation/sphinx/rstFlatTable.py:from docutils.utils import SystemMessagePropagation
Documentation/sphinx/kfigure.py:import os
Documentation/sphinx/kfigure.py:from os import path
Documentation/sphinx/kfigure.py:import subprocess
Documentation/sphinx/kfigure.py:from hashlib import sha1
Documentation/sphinx/kfigure.py:import sys
Documentation/sphinx/kfigure.py:from docutils import nodes
Documentation/sphinx/kfigure.py:from docutils.statemachine import ViewList
Documentation/sphinx/kfigure.py:from docutils.parsers.rst import directives
Documentation/sphinx/kfigure.py:from docutils.parsers.rst.directives import images
Documentation/sphinx/kfigure.py:import sphinx
Documentation/sphinx/kfigure.py:from sphinx.util.nodes import clean_astext
Documentation/sphinx/kfigure.py:from six import iteritems
Documentation/sphinx/kfigure.py:import kernellog
Documentation/sphinx/kfigure.py:    from sphinx.directives.patches import Figure  # pylint: disable=C0413
Documentation/sphinx/cdomain.py:from docutils import nodes
Documentation/sphinx/cdomain.py:from docutils.parsers.rst import directives
Documentation/sphinx/cdomain.py:import sphinx
Documentation/sphinx/cdomain.py:from sphinx import addnodes
Documentation/sphinx/cdomain.py:from sphinx.domains.c import c_funcptr_sig_re, c_sig_re
Documentation/sphinx/cdomain.py:from sphinx.domains.c import CObject as Base_CObject
Documentation/sphinx/cdomain.py:from sphinx.domains.c import CDomain as Base_CDomain

일단 import를 기준으로 검색해 본 결과 six만 없는 것으로 판단했다. 이제 수정하고 commit을 작성하자. 앞에서 조사했던 내용을 포함하여 왜 six가 requirements.txt에 추가되어야 하는지 설명하자.

From eb48c1fd1092cdd0c1636ea0275ab3a48101e483 Mon Sep 17 00:00:00 2001
From: JaeSang Yoo <jsyoo5b@gmail.com>
Date: Mon, 7 Dec 2020 23:35:09 +0900
Subject: [PATCH] docs: update requirements to install six module

On the update of Sphinx version to 2.4.4, the "six" library won't be
installed automatically. (which is required by kfigure.py)

Main reason of this issue were occurred by the requirements changed from
the sphinx library. In Sphinx v1.7.9, six was listed on the
install_requires, but it has been removed since 2.x

The kfigure.py uses six library explicitly, adding six to
requirements.txt seems reasonable

Signed-off-by: JaeSang Yoo <jsyoo5b@gmail.com>
---
 Documentation/sphinx/requirements.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/sphinx/requirements.txt b/Documentation/sphinx/requirements.txt
index 489f6626de67..5030d346d23b 100644
--- a/Documentation/sphinx/requirements.txt
+++ b/Documentation/sphinx/requirements.txt
@@ -1,3 +1,4 @@
 docutils
 Sphinx==2.4.4
 sphinx_rtd_theme
+six
-- 
2.25.1
Sphinx 버전을 2.4.4로 올리면서, "six" 라이브러리가 더 이상 자동으로
설치가 되지 않는다. (kfigure.py에서 필요함)

이 문제의 주요 원인은 sphinx 라이브러리의 요구사항이 변경되었기
때문이다. Sphinx v1.7.9까지는 six가 install_requires에 있었는데,
2.x부터는 삭제되었다.

kfigure.py에서 six 라이브러리를 명시적으로 사용하니깐, six를
requirements.txt에 추가하는 것이 좋겠다.

해당 패치를 커널에 반영하기 위해서는 메일로 패치를 전송해야 하는데, 그건 내용이 복잡하기도 하고, 주제를 따로 빼는 것이 좋을 것 같아 다음 글에서 설명하도록 하겠다.

결과

해당 patch를 메일로 전송했고, 마지막으로 requirements.txt를 수정했던 사람으로부터 답장이 왔다. 사실 1줄밖에 안되는 데다가 간단한 문제기 때문에 특별한 review가 없다.

이후 실제 Documentation의 maintainer가 나한테 patch가 적용되었다고 답장을 보냈다. Python2 하위 호환성을 그만 제공하는 것이 제일 최선이지만, 그 전까지는 six를 설치하는 것이 맞으니 일단 적용하도록 하겠다고 한다. 마침 sphinx 분석 과정에서 현재 버전이 더 이상 python2를 지원하지 않으니, 아예 그냥 python3로 변경시키면 안되는지 물어봤고, 메일을 주고 받은 결과 일단은 더 수정할 거리는 없이 여기서 마무리 하기로 했다.

최종적으로 commit이 mainline에 등록되었다.


JaeSang Yoo
글쓴이
JaeSang Yoo
The Programmer

목차