Makefileの書き方に関する備忘録 その4

他にもMakefileに関する記事を書いていますが、この記事だけで読んでも問題ありません。目次→Makefileの書き方に関する備忘録 - minus9d's diary

少し複雑な構成を持つC++のコード群からバイナリをビルドするための良いMakefileの例を makefile - How to place object files in separate subdirectory - Stack Overflow に見つけたので共有します。

目標

まず、以下のようなソースコードを持っているとします。srcC++ソースコードを格納するディレクトリで、その下のディレクトリ構造は決まった形をもたないものとします。includeはヘッダファイルのみを格納するディレクトリです。

/your_project
  /src
     /subdir1/a.cpp, a.h
              b.cpp, b.h
     /subdir2/subsubdir/c.cpp, c.h
     d.cpp, d.h
  /include
     e.h

これをビルドして以下のようにするのを目標とします。

/your_project
  /src
  /include
  /obj  中間生成物置き場
  /bin  最終バイナリ置き場 

Makefile

前出のページを参考に、自分なりに取捨選択して書き直したMakefileが以下です。このファイルは、your_projectディレクトリの直下に置かれているものとします。

# 参考:https://stackoverflow.com/questions/5178125/how-to-place-object-files-in-separate-subdirectory

# C++のコンパイラ
CXX          := g++

# 生成するバイナリの名前
TARGET      := target

# ディレクトリ名
SRCDIR      := src
BUILDDIR    := obj
TARGETDIR   := bin

# 拡張子名
SRCEXT      := cpp
DEPEXT      := d
OBJEXT      := o

# コンパイル、リンクのフラグ
CXXFLAGS    := -Wall -g -std=c++14
LDFLAGS     :=
INC         := -Isrc -Iinclude

#---------------------------------------------------------------------------------
# DO NOT EDIT BELOW THIS LINE
#---------------------------------------------------------------------------------

sources     := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
objects     := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(subst $(SRCEXT),$(OBJEXT),$(sources)))
dependencies := $(subst .$(OBJEXT),.$(DEPEXT),$(objects))

# Defauilt Make
all: directories $(TARGETDIR)/$(TARGET)

# Remake
remake: cleaner all

# ディレクトリ生成
directories:
    @mkdir -p $(TARGETDIR)
    @mkdir -p $(BUILDDIR)

# 中間生成物のためのディレクトリを削除
clean:
    @$(RM) -rf $(BUILDDIR)

# 中間生成物のためのディレクトリと最終生成物のためのディレクトリを削除
cleaner: clean
    @$(RM) -rf $(TARGETDIR)

# 自動抽出した.dファイルを読み込む
-include $(dependencies)

# オブジェクトファイルをリンクしてバイナリを生成
$(TARGETDIR)/$(TARGET): $(objects)
    $(CXX) -o $(TARGETDIR)/$(TARGET) $^ $(LDFLAGS)

# ソースファイルのコンパイルしてオブジェクトファイルを生成
# また、ソースファイルの依存関係を自動抽出して.dファイルに保存
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
    @mkdir -p $(dir $@)
    $(CXX) $(CXXFLAGS) $(INC) -c -o $@ $<
    @$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
    @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
    @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
    @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
    @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp

# Non-File Targets
.PHONY: all remake clean cleaner

このMakefileでは、"DO NOT EDIT BELOW THIS LINE" より上にある項目を自分の環境に合わせて変更し、makeとコマンドを打つことで、バイナリがbin以下にできるようになります。

また、ソースコードの依存関係も自動で抽出するので、あるファイルを更新したあとmakeすると、そのファイルに依存するすべてのファイルに再コンパイルがかかります。

使い方は以下の通りです。

Pillowで読み込んだ画像に対してChainerCVの検出器を実行

今年の8月、PFNからGitHub - chainer/chainercv: ChainerCV: a Library for Computer Vision in Deep Learningがリリースされました。今のところ2つの物体検出手法(Faster R-CNN, SSD)と1つの画像セグメンテーション手法(SemSeg)が実装されています。examplesフォルダにあるサンプルファイルを実行するだけでお手軽に物体検出を試せるようになっていて、敷居が下がっています。

しかしながら、公式サンプルでは、入力画像を読み込むのに`chainercv.utils.read_image()を使う方法しか示されていないようです。そこで、この記事では、Pillowで読み込んだ画像に対してFaster R-CNNを実行し、自前で検出結果を画像に重畳するサンプルを示したいと思います。

サンプルコード

以下の通りです。Ubuntu 16.04 + Python 3.6で動作を確認しています。

# -*- coding: utf-8 -*-

import argparse

import chainer
from chainercv.datasets import voc_detection_label_names
from chainercv.links import FasterRCNNVGG16
import numpy as np
from PIL import Image, ImageDraw, ImageFont


def convert_pilimg_for_chainercv(pilimg):
    """ pilimg(RGBのカラー画像とする)を、ChainerCVが扱える形式に変換 """ 
    img = np.asarray(pilimg, dtype=np.float32)
    # transpose (H, W, C) -> (C, H, W)
    return img.transpose((2, 0, 1))

def overlay(pilimg, bbox, label, score, label_names=voc_detection_label_names):
    """ pilimgに物体検出結果を重畳。vis_bbox.py を参考に実装 """

    draw = ImageDraw.Draw(pilimg)
    fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 20)

    for i, bb in enumerate(bbox):
        y0, x0, y1, x1 = bb
        draw.rectangle([x0, y0, x1, y1], fill=None, outline='red')

        caption = list()

        if label is not None and label_names is not None:
            lb = label[i]
            if not (0 <= lb < len(label_names)):
                raise ValueError('No corresponding name is given')
            caption.append(label_names[lb])

        if score is not None:
            sc = score[i]
            caption.append('{:.2f}'.format(sc))

        if len(caption) > 0:
            message = ': '.join(caption)
            # テキストを表示するための矩形サイズを取得
            text_width, text_height = fnt.getsize(message)
            # テキストの背後を白く塗る
            draw.rectangle([x0, y0, x0 + text_width, y0 + text_height],
                           fill=(255, 255, 255, 128))
            # テキストを重畳
            draw.text((x0, y0), message, font=fnt, fill='black')


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--gpu', type=int, default=-1)
    parser.add_argument('--pretrained_model', default='voc07')
    parser.add_argument('image')
    args = parser.parse_args()

    model = FasterRCNNVGG16(
        n_fg_class=len(voc_detection_label_names),
        pretrained_model=args.pretrained_model)

    if args.gpu >= 0:
        chainer.cuda.get_device_from_id(args.gpu).use()
        model.to_gpu()

    # PILを使って画像を読み込み
    pilimg = Image.open(args.image).convert('RGB')
    # ChainerCVが扱える形式に変換
    img = convert_pilimg_for_chainercv(pilimg)
    # 物体を検出
    bboxes, labels, scores = model.predict([img])
    bbox, label, score = bboxes[0], labels[0], scores[0]
    # 検出結果を重畳して表示
    overlay(pilimg, bbox, label, score, label_names=voc_detection_label_names)
    pilimg.show()
    # 結果を保存
    pilimg.save('result.jpg')

if __name__ == '__main__':
    main()

処理結果が表示された後、result.jpgという名前で保存されます。 自前で重畳処理をした結果を以下に示します。

f:id:minus9d:20170919223928j:plain

argparseで引数の個数を指定する

Pythonの標準ライブラリargparseで、nargsを使うと、オプションがとる引数の個数を指定できます。例えば、

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--arg1", nargs=3)
args = parser.parse_args()

if args.arg1:
    print(args.arg1)

とすると、オプションarg1はちょうど3個の引数を要求することを意味します。オプションの使用時には、

$ python prog1.py --arg1 a b c

と書けます。このとき、args.arg1には['a', 'b', 'c']が入ります。

同様に、

  • nargs='?':0個または1個の引数を要求
  • nargs='*':0個以上の引数を要求
  • nargs='+':1個以上の引数を要求

という意味になります。

Visual C++にて、CPUにAVX命令があるかどうかを実行中に知る方法

表題のことは、__cpuid, __cpuidexにあるサンプルコードで実現できます。Visual Studio 2017で確認しました。

自分の初代Core i7では、下記のようにAVXに対応していないことがわかります。

GenuineIntel
Intel(R) Core(TM) i7 CPU         870  @ 2.93GHz
3DNOW not supported
3DNOWEXT not supported
ABM not supported
ADX not supported
AES not supported
AVX not supported
AVX2 not supported
AVX512CD not supported
AVX512ER not supported
AVX512F not supported
AVX512PF not supported
BMI1 not supported
BMI2 not supported
CLFSH supported
CMPXCHG16B supported
CX8 supported
ERMS not supported
F16C not supported
FMA not supported
FSGSBASE not supported
FXSR supported
HLE not supported
INVPCID not supported
LAHF supported
LZCNT not supported
MMX supported
MMXEXT not supported
MONITOR supported
MOVBE not supported
MSR supported
OSXSAVE not supported
PCLMULQDQ not supported
POPCNT supported
PREFETCHWT1 not supported
RDRAND not supported
RDSEED not supported
RDTSCP supported
RTM not supported
SEP supported
SHA not supported
SSE supported
SSE2 supported
SSE3 supported
SSE4.1 supported
SSE4.2 supported
SSE4a not supported
SSSE3 supported
SYSCALL not supported
TBM not supported
XOP not supported
XSAVE not supported

g++によるhello world再訪

g++でhello.cppから実行ファイルを生成するときに何が起こっているかを調べてまとめました。全体的に gcc Compilation Process and Steps of C Program in Linux を参考にしています。元記事はCファイルを対象にしていますが、本記事ではC++ファイルを対象としています。

ソースコード

以下のコード hello.cpp をステップバイステップでビルドしていきます。このコードはC++11相当のコードです。

#include <iostream>
#include <vector>
#define MESSAGE "hello  "

int main(void)
{
    std::vector<int> vec{1,2,3,4,5};
    for(auto e: vec) {
        std::cout << MESSAGE << __FILE__ << " " << e << std::endl;
    }
    
    return 0;
}

1. プリプロセッシング

プリプロセッシングの結果を見る

まず最初に、プリプロセッシングと呼ばれる前処理が行われます。具体的には、

  • #includeでインクルードしたヘッダファイルの展開
  • #defineで定義した文字列の置換処理
  • __FILE____LINE__などの記号定数の置換処理

が行われます。(参考:マクロ展開の例

$ cpp -x c++ -std=c++11 hello.cpp > hello.i

または

$ g++ -E -std=c++11 hello.cpp > hello.i

で、プロプロセッシング後のファイルをhello.iという名前で保存できます。

hello.iの抜粋を以下に示します。

# 1 "hello.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.cpp"
# 1 "/usr/include/c++/5/iostream" 1 3
# 36 "/usr/include/c++/5/iostream" 3
...(中略)...
# 5 "hello.cpp"
int main(void)
{
    std::vector<int> vec{1,2,3,4,5};
    for(auto e: vec) {
        std::cout << "hello  " << "hello.cpp" << " " << e << std::endl;
    }

    return 0;
}

ヘッダファイルが検索されるディレクトリの場所を調べる

ヘッダファイルが捜索されるディレクトリの場所は、Cの場合は

$ gcc -xc -E -v -

C++の場合は

$ gcc -xc++ -E -v -

で調べることができます(参考:c++ - What are the GCC default include directories? - Stack Overflow)。私の場合は以下のように表示されました。

()
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/5
 /usr/include/x86_64-linux-gnu/c++/5
 /usr/include/c++/5/backward
 /usr/lib/gcc/x86_64-linux-gnu/5/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

includeされるヘッダファイルのフルパスを調べる

ヘッダファイルは芋づる式にincludeされていくわけですが、具体的にどのパスにあるどのヘッダファイルがincludeされたかは、

$ g++ -std=c++11 -H hello.cpp

で調べることができます(参考:c++ - How to tell where a header file is included from? - Stack Overflow)。私の環境だと以下のようにツリーが表示されました。

$ g++ -H hello.cpp       
. /usr/include/c++/5/iostream
.. /usr/include/x86_64-linux-gnu/c++/5/bits/c++config.h
... /usr/include/x86_64-linux-gnu/c++/5/bits/os_defines.h
.... /usr/include/features.h
..... /usr/include/x86_64-linux-gnu/sys/cdefs.h
...... /usr/include/x86_64-linux-gnu/bits/wordsize.h
..... /usr/include/x86_64-linux-gnu/gnu/stubs.h
...... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h
...(略)...

2. アセンブリコードへの変換

次に、コンパイラは、hello.iアセンブリコードに変換します。

$ g++ -std=c++11 -S hello.i

とすると、アセンブリコードhello.sが生成されます。

        .file   "hello.cpp"
        .section        .rodata
        .type   _ZStL19piecewise_construct, @object
        .size   _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
        .zero   1
        .local  _ZStL8__ioinit
        .comm   _ZStL8__ioinit,1,1
...(略)...

3. オブジェクトファイルへの変換

次に、asコマンドを使って、アセンブリコードhello.sからマシン語に変換し、オブジェクトファイルhello.oを得ます。

$ as hello.s -o hello.o

hello.oの中身は

$ objdump -d hello.o

で調べることができます。以下は実行例です。

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   41 55                   push   %r13
   6:   41 54                   push   %r12
   8:   53                      push   %rbx
   9:   48 83 ec 58             sub    $0x58,%rsp
   d:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
...(略)...

4. リンク

実行ファイルを生成する

最後に、さっき生成したオブジェクトファイルhello.oおよび、他に必要なオブジェクトファイルを集めてリンクし、実行ファイルを生成します。

$ g++ hello.o -o hello

とすると、実行ファイルhelloが生成されます。

ライブラリが検索されるディレクトリの場所、リンクされるライブラリのフルパスを調べる

$ g++ -v hello.o -o hello

とすると、以下のように詳細情報が表示されます。

LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/

は、ライブラリが検索されるディレクトリの場所を表していると思います。

また、

COLLECT_GCC_OPTIONS='-v' '-o' 'hello' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccvdOxv1.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. hello.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

は、リンク時に使われたコマンドの詳細を表していると思います。collect2とはgcc/g++が内部で呼び出すリンクのためのコマンドのようです。ldとの違いはよくわかりません…。

その他の情報

$ g++ -save-temps -std=c++11 hello.cpp

とすると、上記でステップバイステップで生成した中間ファイル hello.ii, hello.s, hello.o が一気に生成されます。

Ubuntuの「壊れた変更禁止パッケージがあります」エラーにaptitudeで対処する方法

Ubuntu 16.04にて、少し前からapt-getで新しいパッケージを入れようとすると「壊れた変更禁止パッケージがあります」エラーが出てインストールできない事象に悩まされていました。

$ sudo apt-get install libgfortran3  
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
インストールすることができないパッケージがありました。おそらく、あり得
ない状況を要求したか、(不安定版ディストリビューションを使用しているの
であれば) 必要なパッケージがまだ作成されていなかったり Incoming から移
動されていないことが考えられます。
以下の情報がこの問題を解決するために役立つかもしれません:

以下のパッケージには満たせない依存関係があります:
 libgfortran3 : 依存: gcc-5-base (= 5.3.1-14ubuntu2) しかし、5.4.0-6ubuntu1~16.04.4 はインストールされようとしています
E: 問題を解決することができません。壊れた変更禁止パッケージがあります。

aptitudeを使ってこの問題への対応を試みました(参考:apt - E: Unable to correct problems, you have held broken packages - Ask Ubuntu)。以下にその方法について記します。自分でもこれが正しい方法なのかよくわかっていません。試す場合は自己責任でお願いします。

$ sudo aptitude install libgfortran3

とすると、以下のように、壊れたパッケージを修正するための改善案が表示されます。改善案を受け入れるならY、他の改善案を探すならnです。

以下の新規パッケージがインストールされます:
  libgfortran3{b} 
0 個のパッケージを更新、 1 個を新たにインストール、 0 個を削除予定、0 個が更新されていない。
260 k バイトのアーカイブを取得する必要があります。 展開後に 1,290 k バイトのディスク領域が新たに消費されます。
以下のパッケージには満たされていない依存関係があります:
 libgfortran3 : 依存: gcc-5-base (= 5.3.1-14ubuntu2) [5.4.0-6ubuntu1~16.04.4 が既にインストール済みです]
以下のアクションでこれらの依存関係の問題は解決されます:

     以下のパッケージを現在のバージョンに一時固定する:
1)     libgfortran3 [インストールされていません]      



この解決方法を受け入れますか? [Y/n/q/?]

私の場合、Yとしてもパッケージをインストールできなかったので、nで次の改善案を探しました。その結果が以下です。

以下のアクションでこれらの依存関係の問題は解決されます:

      以下のパッケージを削除する:                                              
1)      build-essential                                                        
2)      g++                                                                    
3)      g++-5                                                                  
4)      gcc                                                                    
5)      gcc-5                                                                  
6)      libasan2                                                               
7)      libatomic1                                                             
8)      libcilkrts5                                                            
9)      libgcc-5-dev                                                           
10)     libitm1                                                                
11)     liblsan0                                                               
12)     libmpx0                                                                
13)     libstdc++-5-dev                                                        
14)     libtsan0                                                               
15)     libubsan0                                                              

      以下のパッケージをインストールする:                                      
16)     tcc [0.9.27~git20151227.933c223-1 (xenial)]                            

      以下のパッケージをダウングレードする:                                    
17)     cpp-5 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]       
18)     gcc-5-base [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]  
19)     libcc1-0 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]    
20)     libgomp1 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]    
21)     libquadmath0 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]
22)     libstdc++6 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]  

      以下の依存関係を未解決のままにする:                                      
23)     codeblocks が gcc | g++ を推奨                                         
24)     dpkg-dev が build-essential を推奨                                     
25)     cmake が gcc を推奨                                                    


この解決方法を受け入れますか? [Y/n/q/?] 

ここでYとすると、無事所望のパッケージをインストールできました。しかし、今度はgccとg++がアンインストールされ、利用できなくなってしまいました。

sudo aptitude install g++

以下の新規パッケージがインストールされます:
  g++ g++-5{a} gcc{a} gcc-5{a} libasan2{a} libatomic1{a} libc-dev-bin{a} 
  libc6-dev{ab} libcilkrts5{a} libgcc-5-dev{a} libitm1{a} liblsan0{a} 
  libmpx0{a} libstdc++-5-dev{a} libtsan0{a} libubsan0{a} linux-libc-dev{a} 
  manpages-dev{a} 
0 個のパッケージを更新、 18 個を新たにインストール、 0 個を削除予定、0 個が更新されていない。
26.5 M バイトのアーカイブを取得する必要があります。 展開後に 101 M バイトのディスク領域が新たに消費されます。
以下のパッケージには満たされていない依存関係があります:
 libc6-dev : 依存: libc6 (= 2.23-0ubuntu3) [2.23-0ubuntu7 が既にインストール済みです]
以下のアクションでこれらの依存関係の問題は解決されます:

     以下のパッケージを現在のバージョンに一時固定する:    
1)     g++ [インストールされていません]                   
2)     g++-5 [インストールされていません]                 
3)     libc6-dev [インストールされていません]             
4)     libstdc++-5-dev [インストールされていません]       

     以下の依存関係を未解決のままにする:                  
5)     gcc が libc6-dev | libc-dev を推奨                 
6)     gcc-5 が libc6-dev (>= 2.13-0ubuntu6) を推奨       
7)     libgcc-5-dev が libc6-dev (>= 2.13-0ubuntu6) を推奨

Yで答えましたが、まだgccもg++も利用できません。再びsudo aptitude install g++で、いちどnしたあとの

以下のアクションでこれらの依存関係の問題は解決されます:

     以下のパッケージをダウングレードする:                      
1)     libc6 [2.23-0ubuntu7 (now) -> 2.23-0ubuntu3 (xenial)]    
2)     libc6-dbg [2.23-0ubuntu7 (now) -> 2.23-0ubuntu3 (xenial)]

Yで受け入れると、gccとg++が使えるようになりました。

apt-get updateで 「ターゲット ○○ は 複数回設定されています」というエラーに対応する

Ubuntu 16.04でsudo apt-get updateすると、以下のようなエラーが出ました。

W: ターゲット Packages (apps/binary-amd64/Packages) は /etc/apt/sources.list.d/getdeb.list:1 と /etc/apt/sources.list.d/getdeb.list:2 で複数回設定されています
W: ターゲット Packages (apps/binary-i386/Packages) は /etc/apt/sources.list.d/getdeb.list:1 と /etc/apt/sources.list.d/getdeb.list:2 で複数回設定されています
…
W: ターゲット DEP-11 (main/dep11/Components-amd64.yml) は /etc/apt/sources.list.d/google-chrome.list:3 と /etc/apt/sources.list.d/google.list:1 で複数回設定されています
W: ターゲット DEP-11-icons (main/dep11/icons-64x64.tar) は /etc/apt/sources.list.d/google-chrome.list:3 と /etc/apt/sources.list.d/google.list:1 で複数回設定されています

apt - How can I automatically fix W: Target Packages ... is configured multiple times? - Ask Ubuntu にあるとおりPythonスクリプトをダウンロードして実行すると、

$ sudo ./apt-remove-duplicate-source-entries.py 
Overlapping source entries:
  1. /etc/apt/sources.list.d/getdeb.list: deb http://archive.getdeb.net/ubuntu yakkety-getdeb apps
  2. /etc/apt/sources.list.d/getdeb.list: deb http://archive.getdeb.net/ubuntu yakkety-getdeb apps
I disabled the latter entry.

Overlapping source entries:
  1. /etc/apt/sources.list.d/google.list: deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main
  2. /etc/apt/sources.list.d/google-chrome.list: deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main
I disabled the latter entry.


2 source entries were disabled:
  # deb http://archive.getdeb.net/ubuntu yakkety-getdeb apps
  # deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main

Do you want to save these changes? (y/N)

と出たので、yとするとエラーが出なくなりました。実のところ、何が起こっているのかよくわかっていません。試す場合は自己責任でお願いします。