Python - Build python source

2015년 쯤 작성한 것으로 Raspberry Pi, Orange Pi, Odroid SBC 보드에서 3.4를 빌드했다. 2017년 현재 배포본, Debian Jessie, Ubuntu Xeniel 등은 3.4, 3.5가 내장되어 배포되고 있다.
Python 3.6 이상 최신 소스를 빌드하는데 참고할 수 있다.

Build Python 3.4

Raspbian Wheezy에는 3.2가 설치되어 있다. Jessie 가 출시된 이후에 3.2에 대한 모듈 의존성 관리가 되지 않고 있어서 3.4 이상이 설치가 필요하다.

SSL/TLS 지원을 하기 위해 libssl, openssl 라이브러리를 설치한다.

1
2
$ sudo apt update
$ sudo apt install libssl-dev openssl

Python3.4를 다운로드하고 컴파일하고 설치한다.
Raspbian Weezy 에는 Python3.2가 있어서, 최신 3.4를 설치하려면 다운로드해서 컴파일 하면 된다.

1
2
3
4
$ wget https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tgz
$ tar xvzf Python-3.4.3.tgz
$ cd Python-3.4.3/
$ ./configure

Raspberry Pi 2에서 빌드시 꽤 많은 시간을 사용한다.

1
2
3
4
5
$ ./configure
...
real 2m16.512s
user 1m16.360s
sys 0m20.630s

컴파일하고 빌드시 한 시간 이상 필요하다.

1
2
3
4
5
6
$ make                  # 한 시간 소요
$ sudo make install
...
real 16m40.747s
user 15m51.550s
sys 0m24.030s

크로스컴파일 환경을 사용할 수 있다.

Python - Install virtualenv on macOS X

Python 개발환경을 위해서 macOS에 설치된 python2.7 그리고 brew 같은 유틸리티로 python3.x 를 설치하고, pip를 사용해서 패키지를 관리할 수 있다. 그리고 다양한 모듈과 시스템 모듈의 분리를 위해서 버전 관리 도구인 virtualenvvirtualenvwrapper 를 사용해 가상 개발 환경을 구성하는 방법을 설명한다.

자세히 보기

Python - Install virtualenv on Linux

Python 개발환경을 위해서 시스템에 설치된 python2.7, python3.x 에서 사용하는 패키지 모듈을 pip를 사용해서 패키지를 관리할 수 있다. 그리고 시스템 모듈과 별도의 버전 환경으로 버전 관리 도구인 virtualenvvirtualenvwrapper 를 사용해 파이썬 가상 개발 환경을 구성하는 방법을 설명한다.

자세히 보기

NodeJS와 Nginx 웹 서버

Nginx with nodejs

nginx를 사용해서 일반 웹 서비스와 node.js 애플리케이션을 연동하는 방법을 살펴보자.

Nginx 구성

Nginx 설치가 되었다고 가정한다.

nginx 구성 파일

/etc/nginx 밑에 가상 호스트 환경에 맞게 파일을 구성한다.

/etc/nginx/site-available/myhome.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server {

listen 80 default_server;
server_name _;

index index.html index.htm index.nginx-debian.html;

location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;

proxy_pass http://127.0.0.1:50000/;
proxy_redirect off;

try_files $uri $uri/ =404;
}

log_not_found off;

gzip on;
gzip_comp_level 2;
gzip_proxied any;
gzip_min_length 1000;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
}

http 에 대한 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 http {
upstream my_node_app {
server 127.0.0.1:8000;

server {
listen 80;
server_name localhost domain.com;
access_log /var/log/nginx/my_node_app.log;
location ~ /static/ {
root /home/node/my_node_app;
if (!-f $request_filename) {
return 404;
}
}
location / {
proxy_pass http://my_node_app;
proxy_redirect off;

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
}
}
}

nginx 구성을 재시작한다

1
2
3
$ cd /etc/nginx/sites-enabled
$ sudo ln -s /etc/nginx/sites-available/test.conf test.conf
$ sudo service nginx reload

Nginx with Node.js

nodejs 를 연동하기 위한 Proxy 구성을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server {

listen 80;
server_name test.example.com;

location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;

proxy_pass http://127.0.0.1:52222/;
proxy_redirect off;
}

log_not_found off;

gzip on;
gzip_comp_level 2;
gzip_proxied any;
gzip_min_length 1000;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
}

SSL

Nginx를 이용해 SSL 제공을 해주자.

Private SSL 설정

1
2
3
4
5
6
7
$ sudo mkdir /etc/nginx/ssl
$ cd /etc/nginx/ssl
$ sudo openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
$ sudo openssl rsa -passin pass:x -in server.pass.key -out server.key
$ sudo rm server.pass.key
$ sudo openssl req -new -key server.key -out server.csr
$ sudo openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Nginx 설정

/etc/nginx/site-available/yoursite.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
server {
listen 443 default;
server_name erp.yclean.co.kr;

access_log /var/log/nginx/oddo.access.log;
error_log /var/log/nginx/oddo.error.log;

ssl on;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
keepalive_timeout 60;

ssl_ciphers HIGH:!ADH:!MD5;
ssl_protocols SSLv3 TLSv1;
ssl_prefer_server_ciphers on;

proxy_buffers 16 64k;
proxy_buffer_size 128k;

location / {
proxy_pass http://127.0.0.1:8069;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}

location ~* /web/static/ {
proxy_cache_valid 200 60m;
proxy_buffering on;
expires 864000;
proxy_pass http://127.0.0.1:8069;
}
}

server {
listen 80;
server_name erp.yclean.co.kr;

add_header Strict-Transport-Security max-age=2592000;
rewrite ^/.*$ https://$host$request_uri? permanent;
}

http 리다이렉트

http://serverfault.com/questions/250476/how-to-force-or-redirect-to-ssl-in-nginx

1
2
3
4
5
server {
listen 80;
server_name signup.mysite.com;
rewrite ^ https://$server_name$request_uri? permanent;
}

The best way as it described in the official how-to is by using the return directive:

1
2
3
4
5
server {
listen 80;
server_name signup.mysite.com;
return 301 https://$server_name$request_uri;
}

site file 활셩화

1
2
3
$ sudo ln -s /etc/nginx/sites-available/yourOdooSite.com /etc/nginx/sites-enabled/yourOdooSite.com

$ sudo /etc/init.d/nginx restart

CORS

CORS(Cross-Origin resource sharing)은 웹 페이지 도메인 밖의 다른 도메인에서 제한된 웹 페이지를 자원을 허용하도록 하는 메커니즘이다.[^2]

You need to enable CORS on the server (localhost:8080). Check out this site: http://enable-cors.org/

All you need to do is add an HTTP header to the server:

1
Access-Control-Allow-Origin: http://localhost:3000

전체적으로 열어 주려

1
Access-Control-Allow-Origin: *

다음 같이 nginx 설정을 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
et $cors '';
if ($http_origin ~ '^https?://(localhost|www\.yourdomain\.com|www\.yourotherdomain\.com)') {
set $cors 'true';
}

if ($cors = 'true') {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
# required to be able to read Authorization header in frontend
#add_header 'Access-Control-Expose-Headers' 'Authorization' always;
}

if ($request_method = 'OPTIONS') {
# Tell client that this pre-flight info is valid for 20 days
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}

Nginx와 Proxy 서비스

Nginx를 앞단에 두고 Proxy를 이용해 nodeJS, Djang, Angular 등의 서비스를 이용할 때, nginx나 backend 둘 중 한 곳에서 CORS를 활성화 해주면 된다.

Nginx에서 CORS

Nginx에서 CORS를 허용하려면 아래 설정을 사용할 수 있다. [^1]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
}
}

이와 비슷한 방법으로

https://gist.github.com/m4ttbrock/7337183

CORS node.js

https://enable-cors.org/server_expressjs.html

if your app is created with simple node.js set it in your response headers like

1
2
3
4
5
6
7
8
9
10
var http = require('http');

http.createServer(function (request, response) {
response.writeHead(200, {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE'
});
response.end('Hello World\n');
}).listen(3000);

if your app is created with express framework
use a CORS middleware like

1
2
3
4
5
6
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}

cors module

cors 모듈을 이용할 수 있다.

https://github.com/expressjs/cors

Enable All CORS requets
1
2
3
var cors = require("cors");

app.use(cors());
Enable CORS for a Single Route
1
2
3
app.get("/products/:id", cors(), function (req, res, next) {
res.json({ msg: "This is CORS-enabled for a Single Route" });
});
Configuring CORS
1
2
3
4
5
6
7
8
var corsOptions = {
origin: "http://example.com",
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
};

app.get("/products/:id", cors(corsOptions), function (req, res, next) {
res.json({ msg: "This is CORS-enabled for only example.com." });
});

참조

[^1]: CORS on Nginx
[^2]: Cross-origin resource sharing

NodeJS - Patterns

원문 https://darrenderidder.github.io/talks/ModulePatterns/ 요약

Node.js Module Patterns

이것은 같단한 모듈을 hello-module.js 소스에 선언한 것이다.

1
2
// hello-module.js
console.log("Hello World");

선언된 소스는 require() 로 들여올 수 있다:

1
require("hello-module.js");

Define a global

모듈 foo.js 를 글로벌

1
2
3
4
// foo.js
foo = function () {
console.log("Im foo");
};

모듈 foo.js를 들여와 전역에 선언된 함수 foo()를 사용한다.

1
2
require("foo.js");
foo();

그러나 글로벌 영역을 오염 시키지 않는다.

export an anonymous function

module 객체에 export 한다.

1
2
3
4
// bar.js
module.exports = function () {
console.log("Im bar");
};

모듈 bar.js를 들여와 전역에 선언된 함수 foo()를 사용한다.

1
2
var bar = require("./bar.js");
bar();

export a named function

module 객체의 이름 속성으로 export 한다.

1
2
3
4
// bar.js
module.fiz = function () {
console.log("fiz");
};

모듈 bar.js를 들여와 전역에 선언된 함수 foo()를 사용한다.

1
2
var fiz = require("./fiz.js").fiz;
fiz();

export an anonymous object

1
2
3
4
5
6
7
// buz.js
var Buz = function() {};

Buz.prototype.log = function() {
console.log('buz');
}
export.modules = new Buz();

모듈 buz.js를 들여와 객체의 메서드를 호출한다.

1
2
var buz = require("./buz");
buz.log();

export a named object

1
2
3
4
5
6
7
// buz.js
var Baz = function() {};

Baz.prototype.log = function() {
console.log('baz');
}
export.Baz = new Baz();

모듈 baz.js를 들여와 속성 메서드를 호출한다.

1
2
var baz = require("./baz.js").Baz;
baz.log();

export an anonymous prototype

module.exports 에 객체를 노출한다.

1
2
3
4
5
6
7
// doo.js
var Doo = function() {};

Doo.prototype.log = function() {
console.log('doo!');
}
export.exports = new Doo();

모듈 doo.js를 들여와 객체의 메서드를 호출한다.

1
2
3
var Doo = require("./doo.js");
var doo = new Doo();
doo.log();

export a named prototype

module 에 이름 속성을 노출한다.

1
2
3
4
5
6
7
// qux.js
var Qux = function() {};

Qux.prototype.log = function() {
console.log('qux!');
}
export.Qux = Qux;

모듈 qux.js를 들여와 객체의 메서드를 호출한다.

1
2
3
var Qux = require("./qux.js").Qux;
var qux = new Qux();
qux.log();

module.exports

modules.exports는 exports로 가명을 가지고 있다. 이름 있는 속성을 사용할 때 아래 같이 선언한다.

1
2
3
4
> module.exports.fiz = 'Fiz';
> exports.buz = 'buz';
> module.exports === exports;
true

exports 에 직접 대입하면 exports alias를 덮어 쓰게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> module.exports.qux = "qux";
> exports
{ qux: "qux" }
> exports === module.exports
true
> exports = "wobble wibble wubble!";
> exports === module.exports
false
> exports
"wobble wibble wubble!"
> module.exports
{ qux: "qux" }
// module.exports is canonical

pros, cons

  • Named exports: 한 모듈에 여러 개체 및 속성을 노출할 수 있다.
  • Anonymouse exports: 간단한 클라이언트 인터페이스로 적합

MySQL 5.x 시작 (3)

MySQL

사용자 관리

mysql-server 를 설치하며 만든 root 사용자 패스워드를 사용해서 데이터베이스에 접속한다.

1
$ mysql -u root -p

사용자 데이터베이스

사용자가 사용할 데이터베이스를 만든다.

1
2
mysql>create database mydb;
Query OK, 1 row affected (0.00 sec)

그리고 CREATE USER, INSERT 로 새 사용자를 추가할 수 있다.

create user userid@HOST identified by ‘PASSWORD’;

사용자 foo 를 localhost 와 모든 것을 의미하는 패턴 %로 추가하면:

1
2
mysql > create user foo@localhost identified by 'password';
mysql > create user 'foo'@'%' identified by 'password';

혹은

1
insert into user (host, user, password) values ('localhost', 'hiru', 'password('hirururu'));

사용자 제거
mysql > drop user ‘hiru’;
mysql > delete from user where user =’hiru’

사용자 생성시 다음같이 1396 에러는 CREATE USER/GRANT 명령으로 사용자와 권한을 추가/관리해야 하는데 mysql.db, mysql.user 테이블을 직접 조작하다가 일관성이 깨졌기 때문

1
2
mysql> create user 'shopuser'@'localhost' identified by ')12345';
ERROR 1396 (HY000): Operation CREATE USER failed for 'shopuser'@'localhost'

제대로 사용자를 삭제하고

drop user shopuser@localhost

flush privileges; 로 갱신해 준다.

권한 주기

권한을 추가하고 삭제하기 위해서, GRANT와 REVOKE의 명령을 사용한다. GRANT 명령 등으로 데이터베이스 사용자가 데이터베이스 자원에 접근하는 권한을 만들 수 있다.

GRANT ALL privileges ON DB_NAME.TABLE TO USER_ID@[HOST] IDENTIFIED BY ‘PASSWORD’
GRANT [SELECT,DELETE,INSERT,UPDATE,] ON DB_NAME.TABLE TO USER_ID@[HOST] IDENTIFIED BY ‘PASSWORD’

  • DB_NAME,TABLE 등에 * 패턴을 사용할 수 있다.
  • HOST: 접근하는 소스 호스트
  • PASSWORD: 패스워드

http://www.w3big.com/ko/mysql/mysql-administration.html

현재 머신에서만 접속할 수 있는 사용자 계정, 외부, 원격에서 접속할 수 있는 사용자 계정을 추가해 준다.

1
2
3
mysql> use mysql; # mysql system db
mysql> GRANT ALL privileges ON mydb.* TO foo@localhost IDENTIFIED BY '*****';
mysql> GRANT ALL privileges ON mydb.* TO foo@'%' IDENTIFIED BY '*****';

혹은

1
2
grant select, insert, update, delete on mydb.* to foo@host identified by 'password';
mysql > grant select, insert, update, delete on dbname.table to userid@'192.168.%' identified by 'password';

권한을 확인하는 방법

1
2
mysql > show grants for foo@localhost
mysql > show grants for 'foo'@'%';

변경된 권한을 적용하기

1
mysql > flush privileges;

권한을 삭제하는 방법

1
mysql > revoke all on dbname.table from username@host

추가한 사용자는 SELECT로 확인할 수 있다.

1
mysql> select host,authentication_string from user where user='foo';

사용자 데이터베이스 사용

새로 생성한 사용자 ID로 로그인을 해서 데이터베이스 정보를 확인해 보자.

1
2
3
4
5
6
7
8
9
10
$ mysql -u foo -p
Enter password:

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
+--------------------+

그리고 데이터베이스를 사용하려면 use [DATABASE] 로 변경한다.

1
mysql> use mydb;

Character Set

mysql에서 한글이 ?로 표시되는 경우.

1
2
3
4
5
6
7
8
mysql> SELECT * FROM department;
+----+------+
| id | name |
+----+------+
| 1 | ??? |
| 2 | ??? |
+----+------+
2 rows in set (0.00 sec)

my.cnf의 문자셋과 터미널 문자셋이 일치하지 않아서 그렇다. MySQL은 설치시 지정하지 않았다면 기본적으로 문자셋이 ‘latin1’으로 설정되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mysql> show variables like 'c%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
| collation_connection | latin1_swedish_ci |
| collation_database | latin1_swedish_ci |
| collation_server | latin1_swedish_ci |
| completion_type | NO_CHAIN |
| concurrent_insert | AUTO |
| connect_timeout | 10 |
| core_file | OFF |
+--------------------------+----------------------------+
15 rows in set (0.00 sec)

MySQL 설정 파일에서 문자셋을 변경할 수 있다. 다믕 같이 자신의 my.cnf 파일을 작성한다. client, mysqld, mysql 에 대해서 utf8 사용을 선언해 준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[client]
..
default-character-set=utf8

[mysqld]
character-set-client-handshake=FALSE
init_connect="SET collation_connection = utf8_general_ci"
init_connect="SET NAMES utf8"
character-set-server=utf8
collation-server=utf8_general_ci

[mysqldump]
default-character-set=utf8

[mysql]
default-character-set=utf8

mysql> show variables like ‘char%’;
+————————–+—————————-+
| Variable_name | Value |
+————————–+—————————-+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+————————–+—————————-+
8 rows in set (0.00 sec)

사용자 관리

GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
-> ON TUTORIALS.*
-> TO ‘zara‘@’localhost’
-> IDENTIFIED BY ‘zara123’;

SQL

Structural Query Language로 튜플 간의 관계를 계산해서 결과를 도촐한다.

  • SEQUEL: Structured English QUEry Language; part of SYSTEM R, 1974
  • SQL/86: ANSI & ISO standard
  • SQL/89: ANSI & ISO standard
  • SQL/92 or SQL2: ANSI & ISO standard
  • SQL3: in the works…
  • SQL2 supported by ORACLE, SYBASE, INFORMIX, IBM DB2, SQL SERVER, OPENINGRES,…

SQL의 구성

  • SQL consists of the following parts:
  • Data Definition Language (DDL)
  • Interactive Data Manipulation Language (Interactive DML)
  • Embedded Data Manipulation Language (Embedded DML)
  • Views
  • Integrity
  • Transaction Control
  • Authorization
  • Catalog and Dictionary Facilities

교수 학습 연습

데이터 유형

http://www.w3big.com/ko/mysql/mysql-data-types.html

table professor

파일 Python-Database-class-student.sql

컬럼명 테이터 타입 비고
scode varchar(4) 학번, 기본키
sname varchar(20) 이름
sdept varchar(20) 학과
sphone varchar(15) 전화번호
1
2
3
4
5
6
CREATE TABLE professor (
pcode varchar(4) NOT NULL PRIMARY KEY,
pname varchar(10),
pdept varchar(12),
pphone varchar(8)
);
table student

파일 Python-Database-class-student.sql

컬럼명 테이터 타입 비고
scode varchar(4) 학번, 기본키
sname varchar(20) 이름
sdept varchar(20) 학과
sphone varchar(15) 전화번호

create student table

1
2
3
4
5
6
CREATE TABLE student (
scode char(4) NOT NULL PRIMARY KEY,
sname char(10),
sdept char(12),
sphone char(8)
);
table course

파일 Python-Database-class-student.sql

컬럼명 테이터 타입 비고
ccode varchar(4) 과목코드, 기본키
cname varchar(20) 과목명
ctime int 강의 시수
croom varchar(15) 강의실

create course table

1
2
3
4
5
6
CREATE TABLE course (
ccode varchar(4) NOT NULL PRIMARY KEY,
cname varchar(10),
ctime integer,
croom varchar(8)
);
table lecture

파일 Python-Database-class-student.sql

컬럼명 테이터 타입 비고
lpcode varchar(4) 교수코드, 기본키
lccode varchar(4) 과목코드, 기본키

create lecture table

1
2
3
4
5
CREATE TABLE lecture (
lpcode char(4) NOT NULL,
lccode char(4) NOT NULL,
PRIMARY KEY (lpcode, lccode)
);
table advice

파일 Python-Database-class-student.sql

컬럼명 테이터 타입 비고
apcode varchar(4) 교수코드, 기본키
asccode varchar(4) 학번, 기본키

create advice table

1
2
3
4
5
CREATE TABLE advise (
apcode char(4) NOT NULL,
ascode char(4) NOT NULL,
PRIMARY KEY (apcode, ascode)
);
table register

파일 Python-Database-class-student.sql

컬럼명 테이터 타입 비고
rscode varchar(4) 학번, 기본키
rcccode varchar(4) 과목코드, 기본키

create register table

1
2
3
4
5
CREATE TABLE register (
rscode char(4) NOT NULL,
rccode char(4) NOT NULL,
PRIMARY KEY (rscode, rccode)
);

데이터 입력

1
2
3
4
5
6
7
8
9
10
11
12
INSERT INTO professor (pcode, pname, pdept, pphone)\
VALUES ('P001','김 구','컴퓨터공학과','0001');
INSERT INTO professor (pcode, pname, pdept, pphone)\
VALUES ('P002','안창호','컴퓨터공학과','0002');
INSERT INTO professor (pcode, pname, pdept, pphone)\
VALUES ('P003','이육사','국문학과','0003');
INSERT INTO professor (pcode, pname, pdept, pphone)\
VALUES ('P004','박종화','국문학과','0004');
INSERT INTO professor (pcode, pname, pdept, pphone)\
VALUES ('P005','심 훈','사학과','0005');
INSERT INTO professor (pcode, pname, pdept, pphone)\
VALUES ('P006','한용운','사학과','0006');

외부 데이터 파일 이용

파일에 필드 간의 ‘,’로 구분한 데이터 파일이 있다. 물론 필드 구별 문자는 데이터의 내용에 따라 사용자가 임의로 정 할 수 있다.
파일: studens.txt

1
2
3
4
5
6
S001, 박소명, 컴퓨터공학과, 123-4567
S002, 최민국, 컴퓨터공학과, 234-5678
S003, 이승호, 국문학과, 345-6789
S004, 정수봉, 국문학과, 456-7890
S005, 김상진, 사학과, 567-8901
S006, 황정숙, 사학과, 678-9012

studnets.txt 파일을 읽어들이는 것은 스크립트 파일을 작성하거나 mysql에서 직접 실행할 수 있다.
먼저 스크립트 파일 students.sql은 다음과 같다.

1
2
3
use mydb;
load data local infile "student.txt" into table student
fields terminated by ',' ;

이제 mysql 클라이언트에서 데이터를 읽어 들인다.

1
2
mysql>source students.sql;

데이터 조회

SELECT [DISTINCT] select _expr essi on
FROM table_list
WHERE where_definition
ORDER BY col_name [ASC|DESC]
GROUP BY col _name_list
LIMIT [offset ], rows

예제-1) 전체 교수 리스트를 출력하는 SQL 검색 문을 작성하라.
mysql > sel ect * fromprof;

예제-2) 전체 교수 리스트를 이름순서로 출력하는 검색 문을 작성하라.
mysql > sel ect * fromprof order by pname;

예제-5) 전체 교수 리스트를 이름 역순으로 출력하는 검색 문을 작성하라.
mysql > sel ect * fromprof order by pname desc;

전체 교수 리스트를 학과별로 출력하는 검색 문을 작성하라.
mysql > sel ect * fromprof order by pdept, pname;

예제-4) 국문학과 교수 리스트를 이름순서로 출력하는 검색 문을 작성하라.
mysql> select * from professor where pdept =’국문학과’;

JOIN

“FROM 테이블명 AS 별명” 구문은 SQL 문장에서 별명으로 테이블을 참조하 는 역할은 한다.

예제-6) MySQL 과목을 강의하는 교수님의 이름, 전화번호와 강의실을 검색 하는 문장을 작성하라.
mysql> select p.pname, p.pphone, c.croom from professor p, course c, lecture l
where c.cname=’MySQL’ and c.ccode=l.lccode
and l.lpcode=p.pcode;

예제-7) ‘김구’ 교수님이 강의하는 과목명, 강의 시수와 강의실을 검색하는 문장을 작성하라.

1
2
3
4
select c.cname, c.ctime, c.croom
from professor as p, course as c, lecture as l
where p.pname = '김 구' and p.pcode = l.lpcode
and l.lccode = c.ccode;

예제-8) 각 학생이 수강 신청한 과목에 대해서 학생이름, 전화번호, 과목명, 강의실, 강의 시수를 검색하는 문장을 작성하라.

1
2
3
4
select s.sname, s.sphone, c.cname, c.ctime, c.croom
from student as s , course as c, register as r
where s.scode = r.rscode and r.rccode = c.ccode
order by s.sname, c.cname;

sub-query

예제-9) 각 학생이 신청한 총 학점을 구하는 검색식을 작성하라.

1
2
3
4
select s.sname, s.sdept, s.sphone, sum(c.ctime)
from student as s , course as c, register as r
where s.scode = r.rscode and r.rccode = c.ccode
group by s.sname;
  • WHERE 조건절에 해당하는 결과를 GROUP BY 구절에 명시된 s.sname 필드에 따라 그룹으로 결과를 분류하고 난 후, SELECT 필드에 SUM(c.cti me) 함수를 사용해서 c.cti me 필드에 대한 합을 구함으로써 각 학 생이 신청한 총 학점를 구할 수 있다.

예제-10) 각 학과별 교수님은 몇 분인지 구하는 검색식을 작성하라.

1
2
3
select pdept, count(*)
from professor
group by pdept;

LIMIT 구절

예제-11) 페이지 크기가 2 일 때, (예제-8)의 결과에서 두 번째 페이지를 검색하는 SQL문장은 작성하라.
select s.sname, s.sphone, c.cname, c.ctime, c.croom fromstudent as s , course as c, regi ster as r
where s.scode = r.rscode and r.rccode = c.ccode order by s.sname, c.cname
limit 2, 2;

  • 마지막 행의 limit 2, 2구절에서, 첫 번째 인자는 오프셋(offset)으로 검 색 결과 레코드들의 순번을 의미한다. 오프셋 값은 0 부터 지정하기 때문에 오프셋값2는전체레코드중에서세번째레코드를가리킨다. 두번째는 인자는 출력하는 레코드 수(rows)를 의미한다. 따라서, 레코드 수 2 는 2 개 의 레코드를 출력하라는 의미가 된다.

Update

UPDATE tbl_name
SET col_name1 = expr1, col_name2 = expr2, …
[WHERE where_definition] [LIMIT rows];

예제-12) 교수테이블에서 ‘김 구’ 선생님의 이름을 ‘하은용’ 교수님으로 변 경하는 문장을 작성하라.
update prof set pname =’하은용’ where pname =’김구’;

예제-13) 지도 테이블의 교수코드가 ‘P007’ 인 레코드들을 모두 ‘P005’ 로 변경하라.
update advise set apcode =’P005’ where apcode =’P007’;

예제-14) 강의 시수가 2인 과목들의 강의 시수를 하나 증가 시키고, 강의실 을 Lab1로 변경하라.
update course set ctime=ctime + 1, croom=’Lab1’ where ctime=2;

Delete

DELETE FROM tbl_name
[WHERE where_definition] [LIMIT rows]

예제-15 ) 국문학과 학생 레코드를 삭제하는 문장을 작성하라.
delete fromstudent where sdept =’국문학과’;

PyMySQL 튜토리얼

PyMySQL 설치

1
$ pip install PyMySQL

만약 pip 로 설치가 안되면 다음 같이 setup.py를 이용해 직접 설치 할 수 있다.
$ # X.X is the desired PyMySQL version (e.g. 0.5 or 0.6).
$ curl -L https://github.com/PyMySQL/PyMySQL/tarball/pymysql-X.X | tar xz
$ cd PyMySQL*
$ python setup.py install
$ # The folder PyMySQL* can be safely removed now.

1
2
3
4
5
6
CREATE TABLE users (
'id' int(11) NOT NULL AUTO_INCREMENT,
'email' varchar(255) COLLATE utf8_bin NOT NULL,
'password' varchar(255) COLLATE utf8_bin NOT NULL,
PRIMARY KEY ('id')
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;

데이터베이스 삭제

1
mysql> drop database mydb;

사용자 비밀번호 변경

1
mysql> set password for ''myid''@''localhost'' = password(''password'');

테이블 생성

show 명령으로 데이터베이스 자원 현환을 볼 수 있다.

mysql> help show

다음은 데이터베이스 목록을 보고, ‘mydb’를 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

mysql>use mydb
...

테이블 현황

1
2
3
4
5
6
7
mysql> show tables;
+----------------+
| Tables_in_mydb |
+----------------+
| test |
+----------------+
1 row in set (0.00 sec)

테이블 만들기

1
2
3
4
5
6
7
8
mysql>CREATE TABLE users (\
id int(11) NOT NULL AUTO_INCREMENT,\
email varchar(255) COLLATE utf8_bin NOT NULL,\
password varchar(255) COLLATE utf8_bin NOT NULL,\
PRIMARY KEY (id)\
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin\
AUTO_INCREMENT=1 ;

  • mysql> 터미널에서 여러 줄의 명령을 입력하기 위해서 줄의 끝에 ‘'를 사용해서 여러줄을 입력했다.

이럴게 만들어진 테이블은 show 명령으로 작성 스크립트를 확인할 수 있다.

1
2
3
4
5
6
7
8
9
mysql> show create table users;
| Table | Create Table | users | CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) COLLATE utf8_bin NOT NULL,
`password` varchar(255) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin |

1 row in set (0.00 sec)

테이블의 구성 요소는 desc 명령을 확인할 수 있다.

1
2
3
4
5
6
7
8
9
mysql> desc users;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| email | varchar(255) | NO | | NULL | |
| password | varchar(255) | NO | | NULL | |
+----------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

데이터 입력

1
2
3
4
mysql> insert into users values (0, 'aaa@example.com', 'sldfjslfj');
mysql> insert into users values (0, 'bbb@example.com', 'sldfjslfj');
mysql> insert into users values (0, 'ccc@example.com', 'sldfjslfj');
mysql> insert into users values (0, 'ddd@example.com', 'sldfjslfj');

Alter

1
2
3
4
5
6
7
8
mysql> #컬럼 추가
mysql> alter table users add first_name varchar(10);
mysql> alter table users add last_name char(10);
mysql> alter table users add point int(5);
mysql> alter table users add gener int(5);

mysql> #컬럼 삭제
mysql> alter table users drop gener;

참조

MySQL 5.x 소스 빌드 (2)

MySQL

소스 빌드

mysql 계정 생성

groupadd mysql // 시스템에 mysql 그룹 생성

useradd -g mysql -M -s /bin/false mysql // 시스템 로그인이 불가하며 홈디렉터리를 제외하여 mysql 계정을 생성

mysql 소스 파일 다운로드 및 압축 해제

mysql 설치에 필요한 필수 패키지 사전 설치

1
yum install -y cmake bison gcc gcc-c++ ncurses-devel

mysql 데이터베이스 서버를 구축하기 위하여 mysql 최신 버전의 소스를 다운로드 받아 압축을 해제.

1
2
3
4
$ wget http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.28.tar.gz
$ tar xzvf mysql-5.6.28.tar.gz
$ cd mysql-5.6.28

mysql db 설치에 필요한 사전 작업이 완료되면 설치를 진행할 수 있다. Mysql 은 5.5 버전 이후의 버전은 configure 명령어가 아닌 아래와 같이 cmake 명령어를 이용하여 configure 를 진행.

  • mysql cmake command 정리
1
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql - DENABLED_LOCAL_INFILE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 - DWITH_EXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8 - DDEFAULT_COLLATION=utf8_general_ci -Dwith_ZLIB=system - DENABLE_DTRACE=0

mysql 환경 설정파일 및 mysql 초기화

mysql 설치작업이 끝나면 데몬을 구동하기 위한 초기화 작업이 필요하다. 다음과 같이 작업이 완료되면 최초 설치 작업은 마무리 된다.

  • mysql 환경설정 기본 파일 복사
1
$ cp ./support-files/my-default.cnf /etc/my.cnf

mysql 초기화

1
2
$ cd /usr/local/mysql
$ /usr/local/mysql/scripts/mysql_install_db –user=mysql

mysql 서비스 스크립트 및 서비스 설정

Mysql 설정이 완료되면 부가적으로 서비스의 스크립트와, 부팅 시 자동으로 서비스가 올라오도록 아래와 같은 기본 설정을 추가한다.

  • mysql 서비스 스크립트 및 서비스 runlevel 등재
1
2
$ cd /usr/local/mysql
$ cp -a support-files/mysql.server /etc/init.d/mysqld # ln -s /etc/init.d/mysqld /etc/rc3.d/S90mysqld

Start

mysql-server 를 설치하며 만든 root 사용자 패스워드를 사용해서 데이터베이스에 접속한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.26 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)

사용자가 사용할 데이터베이스를 만든다.

1
2
mysql>create database mydb;
Query OK, 1 row affected (0.00 sec)

계속해서 새로운 사용자를 만든다.

1
2
3
4
# 현재 머신에서만 접속할 수 있는 사용자 계정
mysql> GRANT ALL privileges ON *.* TO ID@localhost IDENTIFIED BY '*****';
# 외부, 원격에서 접속할 수 있는 사용자 계정
mysql> GRANT ALL privileges ON *.* TO ID@'%' IDENTIFIED BY '*****';

localhost 머신의 MySQL 데이터베이스에 myid라는 이름의 아이디를 만들고, 패스워드는 password로 설정

사용자 데이터베이스 사용

새로 생성한 사용자 ID로 로그인을 해서 데이터베이스를 만든다.

1
2
3
root@a5d2a69fa410:/# mysql -u qkboo -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \\g.

MySQL 설정

MySQL에서 설정파일을 읽는 순서는 다음과 같다.

/etc/my.cnf
/etc/mysql/my.cnf
/usr/local/mysql/etc/my.cnf
~/.my.cnf

/etc/my.cnf

utf-u 문자셋을 기본으로 설정하기 위해서 my.cnf 파일을 다음 같이 사용한다.

MySQL 설정 파일에서 문자셋을 변경할 수 있다. 다믕 같이 자신의 my.cnf 파일을 작성한다. client, mysqld, mysql 에 대해서 utf8 사용을 선언해 준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[client]
..
default-character-set=utf8

[mysqld]
character-set-client-handshake=FALSE
init_connect="SET collation_connection = utf8_general_ci"
init_connect="SET NAMES utf8"
character-set-server=utf8
collation-server=utf8_general_ci

[mysqldump]
default-character-set=utf8

[mysql]
default-character-set=utf8

참조

MySQL 5.x 설치 (1)

MySQL

Install

Ubuntu 16 x64, armhf 등에서 패키지로 설치

1

Start

mysql-server 를 설치하며 만든 root 사용자 패스워드를 사용해서 데이터베이스에 접속한다.

1
2
3
4
5
6
7
8
9
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.26 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

데이터베이스 보기

1
2
3
4
5
6
7
8
9
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+

mysql shell 에서 root 패스워드 변경

1
2
mysql> update user set password=password('PASSWORD') where user = ‘root’;
mysql> flush privileges;

MySQL 설정

MySQL에서 설정파일을 읽는 순서는 다음과 같다.

/etc/my.cnf
/etc/mysql/my.cnf
/usr/local/mysql/etc/my.cnf
~/.my.cnf

/etc/my.cnf

utf-u 문자셋을 기본으로 설정하기 위해서 my.cnf 파일을 다음 같이 사용한다.

만약 외부에서 데이터베이스를 접속하면 설정의 bind-address 막아 주어야 한다. 그렇지 않으면 클라이언트에서 접속 시도시 다음 2003 에러가 난다.[^2]

1
ERROR 2003 (HY000): Can't connect to MySQL server on

MySQL 설정 파일에서 문자셋을 변경할 수 있다. 다믕 같이 자신의 my.cnf 파일을 작성한다. client, mysqld, mysql 에 대해서 utf8 사용을 선언해 준다.

1
2
3
4
5
6
7
8
9
10
11
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

[mysql.server]
user=mysql
basedir=/var/lib

[safe_mysqld]
err-log=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[client]
..
default-character-set=utf8

[mysqld]
character-set-client-handshake=FALSE
init_connect="SET collation_connection = utf8_general_ci"
init_connect="SET NAMES utf8"
character-set-server=utf8
collation-server=utf8_general_ci

[mysqldump]
default-character-set=utf8

[mysql]
default-character-set=utf8

Mysql Secure Installation

1
# mysql_secure_installation

SSL

SSL을 통한 암호화 접속을 허용하려면 서버측과 클라이언트 측 모두 인증 파일을 만들어야 한다.

https://dev.mysql.com/doc/refman/5.7/en/using-encrypted-connections.html

서버측 인증

인증 --ssl 옵션으로 파일은,

  • –ssl-ca identifies the Certificate Authority (CA) certificate.

  • –ssl-cert identifies the server public key certificate. This can be sent to the client and authenticated against the CA certificate that it has.

  • –ssl-key identifies the server private key.

mysql_ssl_rsa_setup 유틸리티를 실행하면 data 디렉토리 밑에 생성해 준다.[^5]

ca.pem
server-cert.pem
server-key.pem

파일을 생성해 준다.

1
2
3
4
5
[mysqld]

ssl-ca=/var/mysql/ca.pem
ssl-cert=/var/mysql/server-cert.pem
ssl-key=/var/mysql/server-key.pem

openssl 이용

openssl을 이용해 수동으로 키를 생성한다. [^6]

1
2
3
#cd /etc/mysql
#openssl genrsa 2048 > ca-key.pem

ca certificate 생성

1
openssl req -new -x509 -nodes -days 1000 -key ca-key.pem > ca-cert.pem
1
openssl req -newkey rsa:2048 -days 1000 -nodes -keyout server-key.pem > server-req.pem

private key를 생성합니다.

[root@EDYDR51P0 newcerts]# openssl x509 -req -in server-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 > server-cert.pem

증서를 가지고 SSL 로 접속할려면 요런 옵션으로 접속하면 된다.

mysql -u root -p –ssl –ssl-ca=c:\cert\cert.pem

참조

https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-16-04

[^5]: Creating SSL & RSA Certificates and keys
[^6]: [Creating SSL Certificates and keys Using openssl](6.4.3.2 Creating SSL Certificates and Keys Using openssl)

Build ffmpeg - macOS

Mac OS X 에서 ffmpeg 빌드

Android OS에 사용할 ffmpeg를 macOS에서 크로스컴파일러로 빌드해서 포팅하는 과정을 담고 있다.
필요한 것은

  • homebrew
  • xcode command line
  • Android NDK stanalone toolchain

준비할 것

Homebrew 를 이용한 빌드를 위해서 xcode와 homebrew 설치가 필요하다.

Xcode 최신버전들은 Command line tool을 수동으로 설치해야 한다.

  • Xcode를 실행하여 ‘Preferences > Downloads > Command Line Tools’ 항목을 설치

homebrew 설치

1
ruby <(curl -fsSkL raw.github.com/mxcl/homebrew/go)

그리고 homebrew doctor 명령을 실행해 설치 환경과 내용이 이상 없는지 확인한다.

1
homebrew doctor

update로 포뮬라들을 최신으로 갱신해 준다.

1
brew update

homebrew에서 설치하는 개체를 Ruby script로 패키지에 대해 선언한 명세서를 Formula라고 하고 install 명령에 의해 /usr/local/Library/Formula 에 설치한다.

homebrew에서 패키지를 하나 설치한다.

1
$ brew install wget

그리고 패키지의 업그레이드, 제거는 다음과 같다.

1
2
brew upgrade [foo]
brew uninstall [foo]

의존성 패키지 설치

ffmpeg 에 필요한 의존선 패키지를 설치한다.

1
2
3
4
brew install automake celt faac fdk-aac git \
lame libass libtool libvorbis libvpx libvo-aacenc \
opencore-amr openjpeg opus sdl schroedinger shtool \
speex texi2html theora wget x264 xvid yasm

Install libaacplus (atm. there is no recipe for it) 컴파일 환경에 대해 [^1]를 참조했다.

1
2
3
wget http://217.20.164.161/~tipok/aacplus/libaacplus-2.0.2.tar.gz
tar xzf libaacplus-2.0.2.tar.gz
cd libaacplus-2.0.2

libtool on osx is quite different from the gnu libtool, which is called glibtool on osx

1
2
3
4
5
6
7
cat autogen.sh | sed 's/libtool/glibtool/' > autogen2.sh


sed -i '.bck' -e 's/libtool/glibtool/' autogen.sh
./autogen.sh
make && make install
cd ..

Standalone toolchain

http://goo.gl/P20dD

Standalone toolchain은 Android NDK 최근 버전부터 추가된 기능입니다. 이걸 사용하면 ndk-build 명령을 쓰지 않고 기존의 configure -> make를 사용하던 컴파일 과정을 그대로 사용해서 라이브러리를 컴파일 할 수 있습니다.

ndk를 통해서 toolchain을 빌드한다. 자세한 사항은 STANDALONE-TOOLCHAIN  참조한다.

1
2
3
4
$ {NDK}/build/tools/make-standalone-toolchain.sh \
--platform=android-8 \
–install-dir=/MYDEV/android=9-toolchain
$ export PATH=/MYDEV/android=9-toolchain/bin:$PATH

샘플 코드 컴파일 방법
test.cpp 가 있다고 가정하고

1
$ arm-linux-androideabi-g++ -o test_arm test.cpp

Makefile 을 다음과 같이 만든다.

1
2
3
4
5
6
7
8
9
10
ARM_COMPILE = arm-linux-androideabi-
CC = g++

ARM_CC = $(ARM_COMPILE)g++
ARM_INCLUDES = -I /MYDEV/android=9-toolchain/sysroot/usr/include
CFLAGS = -O2 -Wall -D_LINUX -fno-strict-aliasing -D_COLOR_LOG
BINS = test

arm:
$(ARM_CC) $(CFLAGS) -o test_arm test.cpp $(ARM_INCLUDES)

ffmpeg build

standalone toolchain을 사용한다.

ffmpeg configuration

1
export ANDROID_ROOT=/cygdrive/c/my-android-toolchain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ANDROID_ROOT=/home/qkboo/my-android-toolchain \
./configure --target-os=linux \
--arch=arm \
--enable-cross-compile \
--cc=$ANDROID_ROOT/bin/arm-linux-androideabi-gcc \
--cross-prefix=$ANDROID_ROOT/bin/arm-linux-androideabi- \
--extra-cflags="-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon" \
--extra-ldflags="-Wl,--fix-cortex-a8" \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-avdevice \
--disable-network \
--disable-devices \
--disable-filters

맨 첫 줄의 ANDROID_ROOT 값은 자신이 standalone toolchain을 설치한 폴더로 수정합니다.

참고로

  • “–arch=arm”과 “–enable-cross-compile”: arm CPU 용으로 cross compile 하겠다는 옵션입니다.
  • “–cc”나 “–cross-prefix”: cross compile 할 때 사용할 compiler에 관한 정보를 줍니다.
  • “–extra-cflags”나 “–extra-ldflags”는 neon 사용할 때 쓰는 옵션입니다. (c:/android-ndk-r5b/docs/STANDALONE-TOOLCHAIN.html 참조)

나머지는 ffmpeg에서 이러이러한 기능은 빼고 컴파일 하겠다는 뜻입니다. 예를들어 network 이런 기능은 필요없겠지요?

생성된 config.h 파일을 열어봅니다.

1
2
3
4
5
6
7
8
9
10
11
#define ARCH_ARM 1

#define HAVE_ARMV5TE 1

#define HAVE_ARMV6 1

#define HAVE_ARMV6T2 1

#define HAVE_ARMVFP 1

#define HAVE_NEON 1

위와 같은 설정들이 잘 되어 있음을 확인할 수 있으실 겁니다.

config.h 파일에서 #define restrict restrict 부분을 찾아 다음과 같이 바꾼다.

1
#define restrict

X264 컴파일

http://bongjaemoon.wordpress.com/2012/05/25/ffmpeg-x264-compile-for-using-with-android-ndk-on-mac-osx/

Application.mk 작성

g:/Root/FFmpegBasic/jni 폴더에 Application.mk 파일을 만듭니다.
내용은 간단히 아래와 같이 한 줄만 작성합니다.

  1. APP_ABI := armeabi-v7a

참고:
arm architecture ARMv7-A 이상을 타겟으로 컴파일 하겠다는 옵션입니다.
arm CoretexA8 이상의 core가 이에 해당됩니다.
앞서 말씀드린대로 arm11 코어를 사용한 Optimus One, Galaxy Neo 같은 폰에서는 안 돌아가겠지요.

Android.mk 작성

http://www.viper.pe.kr/docs/make-ko/make-ko_toc.html (한글)
http://sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/make_toc.html (영문)

Android.mk 는 폴더마다 여러개를 작성해야 합니다.공통으로 사용할 common.mk 파일을 먼저 작성한 후, 각각 폴더마다 설명하겠습니다.

  • common.mk

g:/Root/FFmpegBasic/jni/ffmpeg 폴더에 common.mk 파일을 만듭니다.모든 Android.mk에서 공통으로 include 해서 사용할 파일입니다.

common.mk에서는 크게 두가지 일을 할 것입니다.1) 공통으로 사용할 컴파일 옵션을 정의합니다.2) configure를 통해 생성된 파일에서 컴파일 할 소스 파일 이름들을 읽어 저장합니다.

  1. 컴파일 옵션은 다음과 같이 한 줄이면 됩니다.

1.COMMON_CFLAGS := -DHAVE_AV_CONFIG_H -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -DPIC -std=c99 -fomit-frame-pointer -fPIC -fno-math-errno -fno-signed-zeros -fno-tree-vectorize
참고:컴파일 옵션이 복잡해 보이지만 그냥 configure에서 생성된 컴파일 옵션을 그대로 정리해 준 것 뿐입니다.

ffmpeg 폴더의 common.mak 파일을 열어보시면아래와 같은 부분이 있습니다.
1.%.o: %.c 2.$(CCDEP)
3.$(CC) $(CPPFLAGS) $(CFLAGS) $(CC_DEPFLAGS) -c $(CC_O) $<
FFmpeg 컴파일 할 때, $(CPPFLAGS) $(CFLAGS) $(CC_DEPFLAGS) 이 세 개의 매크로에 정의된 옵션들을 사용하는 것을 알 수 있습니다.

ffmpeg 폴더의 config.mak 파일을 열어보시면 이 값들이 정의되어 있습니다.

CPPFLAGS는 아래와 같습니다.
1.CPPFLAGS= -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -DPIC
이 값들은 다 사용해 줍니다.

CFLAGS는 엄청 깁니다.
1.CFLAGS= -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon  -std=c99 -fomit-frame-pointer -fPIC -marm -g -Wdeclaration-after-statement -Wall -Wno-parentheses -Wno-switch -Wdisabled-optimization -Wpointer-arith -Wredundant-decls -Wno-pointer-sign -Wcast-qual -Wwrite-strings -Wtype-limits -Wundef -Wmissing-prototypes -Wno-pointer-to-int-cast -O3 -fno-math-errno -fno-signed-zeros -fno-tree-vectorize -Werror=implicit-function-declaration -Werror=missing-prototypes
복잡해 보이지만 하나씩 차근히 보면 정리가 됩니다.
여기서 -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -g -O3 옵션들은 다 뺍니다.우리는 Android ndk-build를 사용할 것이기 때문에 -O3 같은 최적화 관련 옵션은 지정하지 않습니다.(이것은 android build system이 알아서 해줍니다)-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon 이와 같은 cross compile관련, neon 관련 옵션도 빼줍니다.(이것은 나중에 Android.mk의 옵션으로 지정해 줄 것입니다)마지막으로 -W 로 시작하는 옵션은 warning 관련 옵션이니 그냥 다 뺍니다.

CC_DEPFLAGS는 별 것 없고 상관없는 값들입니다. 무시합니다.

추가로 subdir.mak 파일을 보시면 아래와 같은 부분이 있습니다. 1.$(OBJS) $(SUBDIR)%.ho $(SUBDIR)%-test.o $(TESTOBJS): CPPFLAGS += -DHAVE_AV_CONFIG_H
$(OBJS) 에 정의된 모든 파일에 위 조건이 해당되므로 -DHAVE_AV_CONFIG_H 도 포함합니다.

이렇게 정리하면 위에서 한 줄로 정리한 COMMON_CFLAGS 컴파일 옵션들이 나옵니다.

  1. 컴파일 할 소스 파일들을 정의

이 부분은 소스가 좀 길고 복잡하게 느껴질 수 있습니다. 하지만 역시 핵심은 간단합니다.

먼저 FFmpeg의 Makefile을 하나만 분석해 보겠습니다.
ffmpeg 폴더의 common.mak 파일을 열어보면 아래와 같은 부분이 있습니다.
1.OBJS      += $(OBJS-yes)
컴파일에 사용할 소스 파일은 OBJS 매크로와 OBJS-yes 매크로에 정의되어 있다는 것을 알 수 있습니다.
우리도 이 소스들을 컴파일 하면 되므로 똑같이 적어줍니다.

이제 OBJS 매크로에는 xxxxx.o 와 같은 object 파일들이 쭉 저장되게 됩니다.이걸 그냥 간단히 전부 xxxxx.c로 변환해서 쓰면 가장 쉽겠지만 그렇게 간단하지는 않습니다.우선 c 파일 외에도 xxxxx.S 와 같은 어셈블리 코드들이 포함되어 있고,neon 컴파일 해야하는 소스들은 xxxxx.c.neon 또는 xxxxx.S.neon 과 같이 neon 접미사를 붙여줘야 하기 때문입니다.

다행인 것은, FFmpeg 소스들을 보면 neon 컴파일 해야 하는 소스들은 모두
_neon.c 와 같이 _neon 접미사가 붙어 있어서 이것으로 구분이 가능합니다.(ffmpeg/libavcodec/arm 폴더의 파일들을 훑어 보시기 바랍니다)따라서 _neon 접미사를 검색해서 해당 접미사가 있는 소스에만 .neon을 마지막에 추가해 주면 됩니다.

위와 같은 과정을 수동으로 일일이 진행하셔도 좋지만 번거로우니 Makefile 문법을 사용해 작성해 주면 됩니다.최종적으로 컴파일 할 소스 파일들은 각각 다음 매크로에 저장할 것입니다.

C_FILES: 컴파일 할 c 파일S_FILES: 컴파일 할 S 파일NEON_C_FILES: neon 컴파일 할 c 파일NEON_S_FILES: neon 컴파일 할 S 파일FFFILES: 컴파일 할 모든 소스 파일 전부 정의

이제까지 설명한 것을 종합해서 common.mk의 전체 소스를 보여드리면 아래와 같습니다.
common.mk 파일을 다음과 같이 작성해줍니다.

01.COMMON_CFLAGS := -DHAVE_AV_CONFIG_H -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -DPIC -std=c99 -fomit-frame-pointer -fPIC -fno-math-errno -fno-signed-zeros -fno-tree-vectorize
02. 
03.OBJS += $(OBJS-yes)
04. 
05.ALL_S_FILES := $(wildcard $(LOCAL_PATH)/$(ARCH)/.S)
06.ALL_S_FILES := $(addprefix $(ARCH)/,$(notdir $(ALL_S_FILES)))
07. 
08.NEON_S_FILES := $(wildcard $(LOCAL_PATH)/$(ARCH)/
_neon.S)
09.NEON_S_FILES := $(addprefix $(ARCH)/,$(notdir $(NEON_S_FILES)))
10. 
11.NEON_C_FILES := $(wildcard $(LOCAL_PATH)/$(ARCH)/*_neon.c)
12.NEON_C_FILES := $(addprefix $(ARCH)/,$(notdir $(NEON_C_FILES)))
13. 
14.S_FILES := $(filter-out $(NEON_S_FILES),$(ALL_S_FILES))
15. 
16.C_OBJS := $(OBJS)
17.ifneq ($(S_FILES),)
18.S_OBJS := $(S_FILES:.S=.o)
19.S_OBJS := $(filter $(S_OBJS),$(C_OBJS))
20.C_OBJS := $(filter-out $(S_OBJS),$(C_OBJS))
21.else
22.S_OBJS :=
23.endif
24. 
25.ifneq ($(NEON_S_FILES),)
26.NEON_S_OBJS := $(NEON_S_FILES:.S=.o)
27.NEON_S_OBJS := $(filter $(NEON_S_OBJS),$(C_OBJS))
28.C_OBJS := $(filter-out $(NEON_S_OBJS),$(C_OBJS))
29.else
30.NEON_S_OBJS :=
31.endif
32. 
33.ifneq ($(NEON_C_FILES),)
34.NEON_C_OBJS := $(NEON_C_FILES:.c=.o)
35.NEON_C_OBJS := $(filter $(NEON_C_OBJS),$(C_OBJS))
36.C_OBJS := $(filter-out $(NEON_C_OBJS),$(C_OBJS))
37.else
38.NEON_C_OBJS :=
39.endif
40. 
41.C_FILES := $(C_OBJS:.o=.c)
42.S_FILES := $(S_OBJS:.o=.S)
43.NEON_C_FILES := $(NEON_C_OBJS:.o=.c.neon)
44.NEON_S_FILES := $(NEON_S_OBJS:.o=.S.neon)
45. 
46.FFFILES := $(sort $(NEON_S_FILES)) $(sort $(NEON_C_FILES)) $(sort $(S_FILES)) $(sort $(C_FILES))

참고:OBJS 와 OBJS-yes 매크로가 어떻게 생성되는지 보겠습니다.

ffmpeg/libavcodec 폴더의 Makefile을 열어 봅니다.
1.OBJS = allcodecs.o                                                     
2.audioconvert.o                                                  
3.avpacket.o                                                      
4.bitstream.o                                                     
5.bitstream_filter.o                                              
6.dsputil.o                                                       
위와 같은 소스를 볼 수 있습니다.이는 다시 말하면 allcodecs.c, audioconvert.c … 와 같은 소스들은 컴파일 옵션과 상관없이 무조건 컴파일 하겠다는 뜻입니다.

다음으로 아래와 같은 코드들이 이어집니다.
1.OBJS-$(CONFIG_AANDCT)                  += aandcttab.o
2.OBJS-$(CONFIG_AC3DSP)                  += ac3dsp.o
3.OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
ffmpeg 폴더의 config.mak 파일을 열어서 CONFIG_AANDCT, CONFIG_CRYSTALHD 등을 찾아 보시면 이게 어떻게 돌아가는지 알 수 있습니다.config.mak 파일을 열어 보면 아래와 같이 되어 있습니다.
1.CONFIG_AANDCT=yes
2.!CONFIG_CRYSTALHD=yes
즉, “OBJS-$(CONFIG_AANDCT)”는 “OBJS-yes”로 변환되어 aandcttab.c 는 컴파일할 것이고,”OBJS-$(CONFIG_CRYSTALHD)”는 그렇지 않으니 crystalhd.c 는 컴파일 하지 않을 것 입니다.

이런 방법은 거의 모든 open source library에서 사용하고 있는 표준적인 방법이니 익숙해지는 것이 좋습니다.

참조

[^1]: Compiling ffmeg on macOS